@oleksandr-94/aura-css 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +593 -0
  3. package/dist/aura-flat.css +1611 -0
  4. package/dist/aura-flat.min.css +1 -0
  5. package/dist/aura-neu.css +1622 -0
  6. package/dist/aura-neu.min.css +1 -0
  7. package/dist/aura.css +1634 -0
  8. package/dist/aura.min.css +1 -0
  9. package/dist/interactions.d.ts +57 -0
  10. package/dist/interactions.global.min.js +1 -0
  11. package/dist/interactions.min.mjs +1 -0
  12. package/dist/interactions.mjs +208 -0
  13. package/package.json +72 -0
  14. package/src/_config.scss +72 -0
  15. package/src/_functions.scss +23 -0
  16. package/src/_layers.scss +4 -0
  17. package/src/_surface.scss +49 -0
  18. package/src/_tokens.scss +65 -0
  19. package/src/base/_a11y.scss +31 -0
  20. package/src/base/_reset.scss +7 -0
  21. package/src/components/_accordion.scss +62 -0
  22. package/src/components/_alert.scss +43 -0
  23. package/src/components/_avatar.scss +50 -0
  24. package/src/components/_badge.scss +61 -0
  25. package/src/components/_button.scss +79 -0
  26. package/src/components/_card.scss +58 -0
  27. package/src/components/_checkbox.scss +51 -0
  28. package/src/components/_dropdown.scss +73 -0
  29. package/src/components/_file.scss +42 -0
  30. package/src/components/_group.scss +51 -0
  31. package/src/components/_input.scss +85 -0
  32. package/src/components/_modal.scss +43 -0
  33. package/src/components/_pagination.scss +87 -0
  34. package/src/components/_progress.scss +44 -0
  35. package/src/components/_radio.scss +42 -0
  36. package/src/components/_range.scss +27 -0
  37. package/src/components/_rating.scss +43 -0
  38. package/src/components/_segmented.scss +60 -0
  39. package/src/components/_switch.scss +79 -0
  40. package/src/components/_table.scss +38 -0
  41. package/src/components/_tabs.scss +150 -0
  42. package/src/components/_toast.scss +59 -0
  43. package/src/components/_tooltip.scss +60 -0
  44. package/src/index.scss +32 -0
  45. package/src/js/global.js +7 -0
  46. package/src/js/interactions.auto.js +5 -0
  47. package/src/js/interactions.js +192 -0
  48. package/src/skins/_flat.scss +29 -0
  49. package/src/skins/_glass.scss +40 -0
  50. package/src/skins/_neu.scss +49 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 aura-css authors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,593 @@
1
+ # Aura
2
+
3
+ A framework-agnostic, themeable, skinnable CSS component library authored in SCSS.
4
+ Works in any project — plain HTML, React/Vue/Svelte, or alongside Tailwind — via a
5
+ single stylesheet. Theming and surface style ("skin") are decoupled from markup, so the
6
+ same components can be glass in one app and flat in another, on different palettes.
7
+
8
+ > **Status:** early foundation. Complete theming/skin/token system + **2 components**
9
+ > (`button`, `card`). More components are being added on top of this base.
10
+
11
+ ---
12
+
13
+ ## Quick start
14
+
15
+ ```bash
16
+ # from aura/
17
+ npm install # sass + postcss + esbuild toolchain (dev only)
18
+ npm run build # → dist/ : CSS (expanded + min, per skin) + JS bundles + types
19
+ ```
20
+
21
+ Include the stylesheet and set a theme on the root element:
22
+
23
+ ```html
24
+ <html data-theme="aura-dark">
25
+ <head>
26
+ <link rel="stylesheet" href="dist/aura.css" />
27
+ </head>
28
+ <body>
29
+ <button class="btn">Primary</button>
30
+ <div class="card"><h3 class="card__title">Hello</h3></div>
31
+ </body>
32
+ </html>
33
+ ```
34
+
35
+ No build in your consuming project? Just ship the compiled `dist/aura.css`.
36
+
37
+ ---
38
+
39
+ ## The three config levers
40
+
41
+ Everything adapts through `src/_config.scss`. Override with Sass configuration:
42
+
43
+ ```scss
44
+ @use "aura/src/index" with (
45
+ $prefix: 'au-', // namespace all classes → .au-btn (default: none)
46
+ $skin: flat, // surface treatment: glass | flat | neu
47
+ $themes: ( ... ) // your colour palettes (see Theming)
48
+ );
49
+ ```
50
+
51
+ | Lever | What it controls | Default |
52
+ |-------|------------------|---------|
53
+ | `$prefix` | Class-name namespace (avoid collisions with other libs) | `''` |
54
+ | `$skin` | Surface look — glass / flat / neu | `glass` |
55
+ | `$themes` | Colour palettes, swapped at runtime via `data-theme` | dark + light |
56
+
57
+ ---
58
+
59
+ ## Theming
60
+
61
+ Colours follow **[Material 3 color roles](https://m3.material.io/styles/color/roles)**:
62
+ every fill colour has a paired `on-*` content colour for guaranteed contrast, and a
63
+ subtle `*-container` tint for badges/alerts.
64
+
65
+ You only define **base colours** — each `on-*` (text-on-colour) is **derived
66
+ automatically** by luminance, so "white-on-white" is structurally impossible.
67
+
68
+ ### Token contract
69
+
70
+ Components reference **only** these CSS variables (never hardcoded colours):
71
+
72
+ **Accents & status** — each has `--<role>`, `--on-<role>`, `--<role>-container`, `--on-<role>-container`:
73
+ `primary`, `secondary`, `accent`, `success`, `warning`, `error`, `info`
74
+
75
+ **Surfaces & lines**
76
+ | Token | Use |
77
+ |-------|-----|
78
+ | `--surface` | Page background |
79
+ | `--surface-1` / `--surface-2` | Card / elevated (modal, popover) |
80
+ | `--on-surface` / `--on-surface-muted` | Body text / secondary text |
81
+ | `--outline` / `--outline-strong` | Dividers / control borders |
82
+ | `--glass-film` / `--glass-film-2` | Frosted film (glass skin only) |
83
+ | `--shadow-1` / `--shadow-2` | Elevation |
84
+
85
+ **Static scales** (theme-independent)
86
+ `--radius-sm|md|lg|pill` · `--space-1|2|3|4|6|8` · `--font` · `--fs-xs|sm|md|lg|xl` ·
87
+ `--blur` · `--z-dropdown|sticky|modal|toast` ·
88
+ `--gradient` (general brand gradient, auto-built from the theme's accents)
89
+
90
+ ### Add a theme
91
+
92
+ **Quickest:** open `docs/theme-generator.html` — pick accent colours + a base mode, get a
93
+ live preview and a ready-to-paste `[data-theme]` block (with `on-*` and containers derived).
94
+
95
+ Or add an entry to `$themes` — only base colours required:
96
+
97
+ ```scss
98
+ $themes: (
99
+ ocean: (
100
+ primary: #0EA5E9, secondary: #6366F1, accent: #22D3EE,
101
+ success: #16A34A, warning: #D97706, error: #DC2626, info: #3B82F6,
102
+ surface: #0b1220, surface-1: #131c2e, surface-2: #1b2740,
103
+ on-surface: #e6edf7, on-surface-muted: rgba(230,237,247,.62),
104
+ outline: rgba(255,255,255,.14), outline-strong: rgba(255,255,255,.24),
105
+ glass-film: rgba(255,255,255,.06), glass-film-2: rgba(255,255,255,.10),
106
+ shadow-1: (0 8px 32px rgba(0,0,0,.4)), shadow-2: (0 16px 44px rgba(0,0,0,.5)),
107
+ ),
108
+ );
109
+ ```
110
+
111
+ Rebuild, then use it: `<html data-theme="ocean">`. Themes can also be layered per
112
+ subtree — nest `data-theme` on any element.
113
+
114
+ ---
115
+
116
+ ## Skins
117
+
118
+ The **skin** is the surface treatment, independent of colour. Components request their
119
+ chrome via three mixins — `surface()` (panels/cards), `field()` (input wells) and
120
+ `control()` (filled buttons) — and the active skin renders each. So buttons, inputs and
121
+ cards all adapt together.
122
+
123
+ | Skin | Card (`surface`) | Button (`control`) | Input (`field`) |
124
+ |------|------------------|--------------------|-----------------|
125
+ | `glass` | Frosted film + blur | Solid + soft glow + gloss | Translucent well + blur |
126
+ | `flat` | Solid + border | Solid fill | Solid + border |
127
+ | `neu` | Extruded shadows | Extruded, inset on press | Inset well |
128
+
129
+ Switch at build time with `$skin`. Each skin lives in its own file under `src/skins/`;
130
+ adding a new one = new file + one branch in `src/_surface.scss`.
131
+
132
+ Prebuilt skin outputs: `dist/aura.css` (glass, default), `dist/aura-flat.css`,
133
+ `dist/aura-neu.css`.
134
+
135
+ ---
136
+
137
+ ## Components
138
+
139
+ ### Button — `.btn`
140
+
141
+ Base class renders the **primary** button. Add modifiers to change colour, style, size.
142
+
143
+ | Class | Effect |
144
+ |-------|--------|
145
+ | `.btn` | Primary (default) |
146
+ | `.btn--secondary` / `.btn--accent` | Accent colours |
147
+ | `.btn--success` / `.btn--warning` / `.btn--error` / `.btn--info` | Status colours |
148
+ | `.btn--ghost` | Transparent, bordered |
149
+ | `.btn--outline` | Outlined; combine with a colour modifier |
150
+ | `.btn--gradient` | Gradient fill — follows the colour; combine with a colour modifier (`.btn--gradient.btn--success`) |
151
+ | `.btn--sm` / `.btn--lg` | Sizes |
152
+ | `.btn--block` | Full width |
153
+ | `.btn--disabled` / `[disabled]` | Disabled state |
154
+
155
+ States handled automatically: `:hover`, `:active`, `:focus-visible`, `:disabled`.
156
+
157
+ ```html
158
+ <button class="btn">Primary</button>
159
+ <button class="btn btn--secondary">Secondary</button>
160
+ <button class="btn btn--error btn--outline">Delete</button>
161
+ <button class="btn btn--lg btn--block">Continue</button>
162
+ <button class="btn" disabled>Disabled</button>
163
+ ```
164
+
165
+ ### Card — `.card`
166
+
167
+ Surface container rendered through the active skin (glass/flat/neu).
168
+
169
+ | Class | Element / effect |
170
+ |-------|------------------|
171
+ | `.card` | Container (padded, uses `surface()`) |
172
+ | `.card__header` / `.card__title` / `.card__body` / `.card__actions` | Header row / heading / text / footer |
173
+ | `.card--success` / `--warning` / `--error` / `--info` | Coloured status accent (left bar) |
174
+ | `.card--gradient` | Gradient fill; combine with a colour modifier |
175
+
176
+ ```html
177
+ <div class="card card--success">
178
+ <div class="card__header">
179
+ <h3 class="card__title">Acme Inc.</h3>
180
+ <span class="badge badge--success">Active</span>
181
+ </div>
182
+ <p class="card__body">Enterprise plan · renews 12 Aug.</p>
183
+ </div>
184
+
185
+ <div class="card card--gradient card--error"> … </div>
186
+ ```
187
+
188
+ ### Input — `.input` / `.select` / `.textarea`
189
+
190
+ Text controls whose "well" is rendered through the `field()` mixin, so they adopt the
191
+ active skin (glass / flat / neu). Wrap with `.field` to attach a label and hint.
192
+
193
+ | Class | Effect |
194
+ |-------|--------|
195
+ | `.input` / `.select` / `.textarea` | Base controls |
196
+ | `.input--sm` / `.input--lg` | Sizes |
197
+ | `.input--error` (also `--select`/`--textarea`) | Error state (red border) |
198
+ | `.field` | Labelled wrapper (label + control + hint) |
199
+ | `.field__label` | Label |
200
+ | `.field__hint` / `.field__hint--error` | Helper / error text |
201
+
202
+ States handled automatically: `:focus-visible` (primary ring), `:disabled`.
203
+
204
+ ```html
205
+ <div class="field">
206
+ <label class="field__label" for="email">Email</label>
207
+ <input id="email" class="input" type="email" placeholder="you@studio.dev">
208
+ </div>
209
+
210
+ <div class="field">
211
+ <label class="field__label" for="pw">Password</label>
212
+ <input id="pw" class="input input--error" type="password">
213
+ <span class="field__hint field__hint--error">At least 8 characters.</span>
214
+ </div>
215
+ ```
216
+
217
+ ### Badge — `.badge`
218
+
219
+ Small status label. Colour variants use soft `*-container` tints with a guaranteed-contrast
220
+ content colour.
221
+
222
+ | Class | Effect |
223
+ |-------|--------|
224
+ | `.badge` | Base (neutral) |
225
+ | `.badge--primary` / `--secondary` / `--accent` | Accent tints |
226
+ | `.badge--success` / `--warning` / `--error` / `--info` | Status tints |
227
+ | `.badge--outline` | Transparent, bordered |
228
+ | `.badge--sm` / `.badge--lg` | Sizes |
229
+ | `.badge__dot` | Leading status dot |
230
+
231
+ ```html
232
+ <span class="badge badge--success"><i class="badge__dot"></i> Active</span>
233
+ <span class="badge badge--error badge--outline">Failed</span>
234
+ ```
235
+
236
+ ### Alert — `.alert`
237
+
238
+ A message panel with an accent bar and icon. Colour variants carry status meaning.
239
+
240
+ | Class | Effect |
241
+ |-------|--------|
242
+ | `.alert` | Base (primary) panel |
243
+ | `.alert--success` / `--warning` / `--error` / `--info` | Status colours |
244
+ | `.alert__icon` | Leading icon |
245
+ | `.alert__title` / `.alert__body` | Bold heading / muted description |
246
+
247
+ ```html
248
+ <div class="alert alert--error">
249
+ <span class="alert__icon">✕</span>
250
+ <div>
251
+ <div class="alert__title">Payment failed</div>
252
+ <div class="alert__body">Your card was declined.</div>
253
+ </div>
254
+ </div>
255
+ ```
256
+
257
+ ### Switch — `.switch`
258
+
259
+ A toggle for boolean settings. The track is a skin-aware well; when checked it fills with
260
+ the accent colour and shows a white thumb.
261
+
262
+ | Class | Effect |
263
+ |-------|--------|
264
+ | `.switch` | Toggle (wraps a checkbox) |
265
+ | `.switch__track` / `.switch__thumb` | Track / knob |
266
+ | `.switch--success` / `--warning` / `--error` | Checked colour |
267
+ | `.switch--sm` / `.switch--lg` | Sizes |
268
+
269
+ ```html
270
+ <label class="switch">
271
+ <input type="checkbox" checked>
272
+ <span class="switch__track"></span>
273
+ <span class="switch__thumb"></span>
274
+ </label>
275
+ ```
276
+
277
+ ### Checkbox — `.checkbox` · Radio — `.radio`
278
+
279
+ Custom controls set directly on the native input. The box/circle is a skin-aware well;
280
+ checked fills with the accent colour + white mark. `:indeterminate` (checkbox) shows a dash.
281
+
282
+ | Class | Effect |
283
+ |-------|--------|
284
+ | `.checkbox` / `.radio` | On the native input |
285
+ | `.checkbox--success` / `--warning` / `--error` | Checked colour |
286
+ | `.radio--success` / `--warning` / `--error` | Checked colour |
287
+
288
+ ```html
289
+ <input type="checkbox" class="checkbox" checked>
290
+ <input type="radio" name="plan" class="radio radio--success" checked>
291
+ ```
292
+
293
+ ### Select — `.select`
294
+
295
+ Native `<select>` with a custom chevron. Same sizes (`--sm`/`--lg`) and validation
296
+ (`--error`/`--success`) as text inputs.
297
+
298
+ ### Range — `.range` · File — `.file` · Rating — `.rating`
299
+
300
+ | Class | Effect |
301
+ |-------|--------|
302
+ | `.range` | Themed slider · `--success`/`--warning`/`--error` · `--sm`/`--lg` |
303
+ | `.file` | Styled file input · `.file--ghost` |
304
+ | `.rating` | Star rating on radios (CSS-only, submittable) · `--sm`/`--lg` |
305
+
306
+ ```html
307
+ <input class="range range--success" type="range" value="80">
308
+ <input class="file" type="file">
309
+ <div class="rating">
310
+ <input type="radio" name="r" value="5"><label>★</label>
311
+ <input type="radio" name="r" value="4" checked><label>★</label>
312
+
313
+ </div>
314
+ ```
315
+
316
+ **Validation** — add `--error` / `--success` to any control; use `.field__label--required`
317
+ (adds `*`) and `.field__hint--error` / `--success` for messages.
318
+
319
+ ### Tabs — `.tabs`
320
+
321
+ A tab bar with switchable panels. Point each `.tab` at a panel with `data-tab="#id"`; the
322
+ JS activates it and toggles panel visibility. Frameworks render active tab + panel from state.
323
+
324
+ | Class | Effect |
325
+ |-------|--------|
326
+ | `.tabs` | Container: pill (default) · `--underline` / `--lift` · `--sm` / `--lg` · `--block` |
327
+ | `.tab` / `.tab--active` | Tab item / selected |
328
+ | `.tab-panel` | Content panel (toggled via `hidden`) |
329
+ | `.tab-panels` | Panel container (plain by default; card after `--lift` tabs) |
330
+ | `.tab-card` | Wraps `.tabs` + `.tab-panels` into one connected card (no gap) |
331
+
332
+ ```html
333
+ <div class="tabs" role="tablist">
334
+ <button class="tab tab--active" data-tab="#p1">Overview</button>
335
+ <button class="tab" data-tab="#p2">Activity</button>
336
+ </div>
337
+ <div id="p1" class="tab-panel">Overview…</div>
338
+ <div id="p2" class="tab-panel" hidden>Activity…</div>
339
+ ```
340
+
341
+ ### Group — `.group` (alias `.btn-group`)
342
+
343
+ Joins adjacent controls — buttons **and** form fields — into one segmented unit (square
344
+ inner corners, single shared border). Fields grow, buttons stay natural. `.group--block`
345
+ for full width.
346
+
347
+ ```html
348
+ <!-- button group -->
349
+ <div class="group">
350
+ <button class="btn btn--ghost">Day</button>
351
+ <button class="btn btn--ghost">Week</button>
352
+ </div>
353
+
354
+ <!-- input group -->
355
+ <div class="group">
356
+ <input class="input" placeholder="Search…">
357
+ <button class="btn">Search</button>
358
+ </div>
359
+ ```
360
+
361
+ **Radio segmented** — for single-select, `.segmented` uses native radios (CSS-only,
362
+ keyboard-accessible, form-submittable); the checked pill is skin-aware.
363
+
364
+ ```html
365
+ <div class="segmented" role="radiogroup">
366
+ <label class="segmented__option"><input type="radio" name="v" checked><span>Day</span></label>
367
+ <label class="segmented__option"><input type="radio" name="v"><span>Week</span></label>
368
+ </div>
369
+ ```
370
+
371
+ ### Table — `.table`
372
+
373
+ Transparent data table (place in a `.card`). `.table--zebra` for stripes, `.table--compact` for tighter rows.
374
+
375
+ ```html
376
+ <table class="table table--zebra"> … </table>
377
+ ```
378
+
379
+ ### Progress — `.progress` · Spinner — `.spinner`
380
+
381
+ | Class | Effect |
382
+ |-------|--------|
383
+ | `.progress` / `.progress__bar` | Track / fill (set width inline) |
384
+ | `.progress--success` / `--warning` / `--error` | Fill colour |
385
+ | `.spinner` | Loading spinner · `--sm` / `--lg` |
386
+
387
+ ```html
388
+ <div class="progress"><span class="progress__bar" style="width:72%"></span></div>
389
+ <span class="spinner"></span>
390
+ ```
391
+
392
+ ### Tooltip — `.tooltip`
393
+
394
+ Pure-CSS tooltip on hover/focus, inverted vs. the theme. `.tooltip--bottom` to flip placement.
395
+
396
+ ```html
397
+ <span class="tooltip">
398
+ <button class="btn">Hover me</button>
399
+ <span class="tooltip__text">Helpful hint</span>
400
+ </span>
401
+ ```
402
+
403
+ ---
404
+
405
+ ## Interactive components (Modal, Dropdown)
406
+
407
+ Stateful components keep their state **in the DOM** as `data-state="open|closed"`; CSS
408
+ renders it. Nothing in the contract is tied to the library name.
409
+
410
+ - **Vanilla** — load the optional behaviour layer; it auto-wires triggers and adds
411
+ scroll-lock, focus-trap, Esc and outside/backdrop close:
412
+ ```html
413
+ <script type="module" src="src/js/interactions.auto.js"></script>
414
+ ```
415
+ Triggers: `data-open="#id"`, `data-close`, `data-toggle`. (Prefix them via
416
+ `configure({ prefix: 'ui' })` → `data-ui-open`.)
417
+ - **React / Vue / anything** — no library JS needed. Bind `data-state` to your own state
418
+ and handle clicks your way. Optionally import pure helpers:
419
+ `import { open, close, trapFocus, lockScroll } from './src/js/interactions.js'`.
420
+
421
+ ### Modal — `.modal-overlay` + `.modal`
422
+
423
+ Mark the overlay `data-overlay` so the JS applies scroll-lock + focus-trap + backdrop-close.
424
+
425
+ ```html
426
+ <button class="btn" data-open="#dlg">Open</button>
427
+ <div class="modal-overlay" id="dlg" data-state="closed" data-overlay>
428
+ <div class="modal" role="dialog" aria-modal="true">
429
+ <h3 class="modal__title">Title</h3>
430
+ <p class="modal__body">Body…</p>
431
+ <div class="modal__actions"><button class="btn btn--ghost" data-close>Close</button></div>
432
+ </div>
433
+ </div>
434
+ ```
435
+
436
+ ### Dropdown — `.dropdown`
437
+
438
+ ```html
439
+ <div class="dropdown" data-state="closed">
440
+ <button class="btn" data-toggle>Menu <span class="dropdown__caret"></span></button>
441
+ <div class="dropdown__menu">
442
+ <a class="dropdown__item" href="#">Profile</a>
443
+ <div class="dropdown__divider"></div>
444
+ <a class="dropdown__item" href="#">Sign out</a>
445
+ </div>
446
+ </div>
447
+ ```
448
+
449
+ ### Avatar — `.avatar`
450
+
451
+ Image or initials, with `--sm`/`--lg`/`--square`, an `.avatar__status` dot, and an
452
+ overlapping `.avatar-group`.
453
+
454
+ ### Accordion — `.accordion`
455
+
456
+ Built on native `<details>` — zero JS. Add `name="…"` to items to make them exclusive.
457
+
458
+ ```html
459
+ <div class="accordion">
460
+ <details class="accordion__item" name="faq" open>
461
+ <summary class="accordion__head">Question</summary>
462
+ <div class="accordion__body">Answer…</div>
463
+ </details>
464
+ </div>
465
+ ```
466
+
467
+ ### Toast — `.toast`
468
+
469
+ Show imperatively with the JS helper (auto-creates the container, auto-dismisses):
470
+
471
+ ```js
472
+ import { toast } from './dist/interactions.mjs';
473
+ toast('Changes saved.', { type: 'success', timeout: 4000 });
474
+ ```
475
+
476
+ ### Pagination — `.pagination`
477
+
478
+ `.pagination__item` for pages/arrows; `--active` (skin-aware) / `--disabled`.
479
+ Variants: `.pagination--joined` (button-group style, shared borders) · `.pagination--sm`.
480
+ Use `.pagination__ellipsis` for `…` gaps in long ranges.
481
+
482
+ ---
483
+
484
+ ## Distribution
485
+
486
+ `npm run build` produces everything into `dist/` — Sass compiles the CSS (expanded +
487
+ minified per skin), PostCSS/autoprefixer adds vendor prefixes (targets from the
488
+ `browserslist` field), and esbuild bundles the JS.
489
+
490
+ **CSS** — one file per skin, expanded and minified:
491
+
492
+ | Skin | Expanded | Minified |
493
+ |------|----------|----------|
494
+ | glass (default) | `dist/aura.css` | `dist/aura.min.css` |
495
+ | flat | `dist/aura-flat.css` | `dist/aura-flat.min.css` |
496
+ | neu | `dist/aura-neu.css` | `dist/aura-neu.min.css` |
497
+
498
+ **JS** (optional behaviour layer):
499
+
500
+ | File | Use |
501
+ |------|-----|
502
+ | `dist/interactions.mjs` / `.min.mjs` | ESM — `import { open, toast } from '@oleksandr-94/aura-css'` (frameworks, `<script type=module>`) |
503
+ | `dist/interactions.global.min.js` | Minified IIFE — `<script src>` exposes `window.Aura` + auto-wires triggers |
504
+ | `dist/interactions.d.ts` | TypeScript types |
505
+
506
+ The global name is esbuild's `--global-name` flag — rename freely; the DOM contract is
507
+ unaffected.
508
+
509
+ **Package entry points** (`exports`):
510
+
511
+ ```js
512
+ import { open, toast } from '@oleksandr-94/aura-css'; // JS (ESM) + types
513
+ import '@oleksandr-94/aura-css/css'; // glass CSS (also: @oleksandr-94/aura-css/css/min)
514
+ import '@oleksandr-94/aura-css/flat'; // flat skin (also: @oleksandr-94/aura-css/flat/min)
515
+ import '@oleksandr-94/aura-css/neu'; // neu skin
516
+ // SCSS source for custom builds: @use '@oleksandr-94/aura-css/scss' with ($skin: flat, $prefix: 'ui');
517
+ ```
518
+
519
+ ---
520
+
521
+ ## Accessibility
522
+
523
+ - **Reduced motion** — Aura component transitions/animations are neutralised under
524
+ `prefers-reduced-motion: reduce` (in the last cascade layer, so it never touches the
525
+ host app's own motion).
526
+ - **Keyboard** — Modal: focus-trap, Esc, focus return. Dropdown: `↑`/`↓`/`Home`/`End`
527
+ over items, Esc, `aria-expanded`, focus return. Tabs: `←`/`→` roving focus.
528
+ - **Screen readers** — Toasts announce via an `aria-live="polite"` region (errors use
529
+ `role="alert"`); a `.sr-only` helper is provided for visually-hidden labels.
530
+ - **Focus visible** — every interactive component has a `:focus-visible` ring.
531
+
532
+ ## Browser support
533
+
534
+ Aura targets an **evergreen baseline** (Chrome/Edge 111+, Firefox 113+, Safari 16.4+),
535
+ which is required for:
536
+
537
+ | Feature | Used for | If missing |
538
+ |---------|----------|------------|
539
+ | `color-mix()` | tints, hovers, neu shadows | no practical polyfill — hard requirement |
540
+ | Cascade layers (`@layer`) | override control | hard requirement |
541
+ | `backdrop-filter` | **glass skin only** | glass loses blur (film stays); use `flat`/`neu` |
542
+
543
+ These features are the same generation, so there is no meaningful fallback to add — the
544
+ baseline is documented rather than polyfilled. Non-glass skins (`flat`, `neu`) have the
545
+ lightest requirements.
546
+
547
+ ---
548
+
549
+ ## Architecture
550
+
551
+ ```
552
+ aura/
553
+ ├── src/ # library source
554
+ │ ├── _config.scss # the 3 levers: $prefix, $skin, $themes
555
+ │ ├── _functions.scss # auto on-colour (contrast) derivation
556
+ │ ├── _tokens.scss # emits static scales + generates every theme
557
+ │ ├── _surface.scss # skin dispatcher: surface() / field()
558
+ │ ├── skins/ # _glass · _flat · _neu
559
+ │ ├── base/_reset.scss # minimal reset in @layer base
560
+ │ ├── components/ # _button · _card
561
+ │ ├── _layers.scss # cascade-layer order
562
+ │ └── index.scss # entry (default = glass)
563
+ ├── builds/ # build presets (recipes): flat.scss …
564
+ ├── dist/ # compiled CSS output
565
+ ├── demo/ # live demo page
566
+ └── package.json
567
+ ```
568
+
569
+ - **Cascade layers** — all Aura CSS lives in `@layer base, components`; your app's own
570
+ unlayered styles always win, so overriding is easy.
571
+ - **Coexistence** — set `$prefix` to avoid class clashes with other libraries. With
572
+ Tailwind v4 you can also map Aura's role variables into `@theme` to share one palette.
573
+
574
+ ---
575
+
576
+ ## Testing
577
+
578
+ Unit tests cover the JS behaviour layer (`interactions.js`) with Vitest + happy-dom:
579
+
580
+ ```bash
581
+ npm test # run once
582
+ npm run test:watch
583
+ ```
584
+
585
+ Covered: open/close/toggle, `mount()` trigger delegation, `activateTab`, ref-counted
586
+ scroll-lock, `toast()` lifecycle, and prefix config.
587
+
588
+ ## Not yet (roadmap)
589
+
590
+ - Components: `breadcrumbs`, `menu`/sidebar, `steps`, `skeleton`, `stat`, `drawer`, `popover`.
591
+ - More prebuilt themes (the token system supports them cheaply).
592
+ - Visual-regression tests (Playwright over the docs, per skin × theme) + a11y audit (axe).
593
+ - RTL audit · `prefers-color-scheme` auto-theme · per-component CSS imports (tree-shaking).