@arag 0.1.0
Plain CSS · no build step · no dependencies

One link tag styles MONT, TAAY, ARDX, the kiosk app and the mobile app

A house CSS design system built on native cascade layers, oklch() colour and light-dark() dark mode. Bilingual Latin and Mongolian Cyrillic from the first line. Nothing to install, nothing to compile, nothing to run.

The whole install:

<link rel="stylesheet" href="node_modules/@arag/css/src/index.css">

Shipping in five surfaces

  • MONT
  • TAAY
  • ARDX
  • Kiosk
  • Mobile

The whole surface, counted

Every number here is asserted in CI. If the package and the documentation ever disagree, the build fails.

Custom properties
293
Components
16
Utility classes
161
Dependencies
0

The premise

Every design system eventually grows a build step, a config file and a class scanner. This one refuses all three, and gives up nothing to do it.

What the preprocessors were for, the platform now does. @layer, custom properties, nesting, color-mix(), oklch() and light-dark() all ship in Chrome, Edge, Firefox and Safari today. The package is plain CSS because plain CSS became enough.

Six decisions the rest follows from

Overriding is cascade order, never force

Six layers, declared once

The order is reset, tokens, base, components, utilities, overrides. Your application CSS is unlayered, which places it above all six — so plain authoring wins by default, with no specificity trick and no escalation. There is not one priority-raising declaration in the package, and that is greppable.

How the layer model works →

Five ramps of eleven steps, each one hue plus a shared lightness ladder plus its own chroma ladder. Change --hue-brand and all eleven steps regenerate. Hover and active shades derive from their base with color-mix() — no hand-written state colours exist.

Read the colour system →

light-dark() resolves against the used color-scheme, so the manual override is a plain [data-theme] attribute that works on any subtree. The hero at the top of this page is a dark region on a page in your own theme — same tokens, nothing overridden.

Read the theming model →

Mongolian Cyrillic runs 15–25% longer than the Latin source. Nothing in the package assumes a single line or a fixed width, and no translated string is ever truncated, clamped or held to one line — an ellipsis eats the case suffix that carries the grammatical meaning. The one carve-out is a run of digits, which is not translated and is misread if it breaks.

Read the constraint →

Bare BEM class names, so your CSS wins a collision. Variants and sizes are data-attributes, so two variants can never apply at once. Native state stays native. Every component exposes a namespaced custom property API, so a variant re-points four values instead of restating a block.

Browse all sixteen →

A static file, written by hand. No generator, no arbitrary-value syntax, no scanner and no config. If a class cannot be named, it is not needed — which is why there is no fixed-width utility and no truncation utility, and never will be.

See the whole list →

Nothing below is a screenshot

Every control here is real markup styled by the same src/index.css this page links. None of it needs JavaScript to render, and none of it needs any to keep working.

button · 7 variants, 3 sizes

badge · 5 variants, 2 emphases

Active Идэвхтэй Pending Overdue 12

alert · 4 variants

Payment updated

Төлбөрийн мэдээлэл амжилттай шинэчлэгдлээ.

progress · native <progress>

collapse · native <details>, no script

Данс хэрхэн нээх вэ?

Open it, close it, reload with scripting disabled. It behaves the same, because the disclosure is the browser's and not ours.

table · numeric columns align on the attribute

Recent transfers
Account Amount
Шинэ захиалга1,250,000
Kiosk float84,500
Гүйлгээний шимтгэл1,200

Both scripts, both themes

The same components, in the language they ship in

Those two panels are one page in one theme. Each is a subtree with its own color-scheme, and every token underneath re-resolves.

Light

Create a new order

Шинэ захиалга үүсгэх

Dark

Create a new order

Шинэ захиалга үүсгэх

The constraint

Cyrillic runs roughly 15–25% longer than the equivalent Latin string, and the case suffix that carries the grammatical meaning sits at the end of the word. That is why no translated string in this package is truncated, clamped or forbidden to wrap — the ellipsis would eat the grammar. The sole exception is a numeral run, in money and in numeric table cells: digits are not translated, and 1,250,000 broken across two lines is read as two numbers.

Why your override already wins

Highest priority first. Unlayered CSS beats every layer, regardless of specificity — so the thing you write in your own app is the thing that applies.

  1. your app CSS unlayered — beats all six, by rule
  2. overrides reserved, empty in the package
  3. utilities 161 static classes
  4. components 16 files, one per component
  5. base element defaults
  6. tokens 293 custom properties
  7. reset token-free by design

Add one line and the system is on

Chrome and Edge 123+, Firefox 120+, Safari 17.5+ — the maximum, per engine, of every feature the package uses. The binding constraint is light-dark().