@akonwi/mica 0.6.0 → 0.7.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.
package/README.md CHANGED
@@ -2,9 +2,11 @@
2
2
 
3
3
  **Custom elements. Native behavior. Nearly no JavaScript.**
4
4
 
5
- Mica is a front-end library built from custom element tags, native HTML
6
- elements, and CSS. No runtime, no build step, no framework. View source —
7
- there's nothing there but HTML and a stylesheet.
5
+ Mica is a **progressive component library** of custom elements
6
+ progressive as in progressive enhancement: every component starts from
7
+ markup that works, and each layer (CSS, the browser's native behavior, an
8
+ optional JS module) enhances it. No runtime, no build step, no framework.
9
+ View source — there's nothing there but HTML and a stylesheet.
8
10
 
9
11
  ```html
10
12
  <link rel="stylesheet" href="mica.css">
@@ -47,18 +49,18 @@ import "@akonwi/mica/mica.css";
47
49
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@akonwi/mica@0.2/mica.css">
48
50
  ```
49
51
 
50
- ## The three tiers
52
+ ## Progressive by design
51
53
 
52
54
  Every component states where its behavior comes from
53
- (see [TIERS.md](TIERS.md)):
55
+ (see [PROGRESSIVE.md](PROGRESSIVE.md)):
54
56
 
55
- - **Tier 0 — CSS is the behavior.** Layout primitives: `m-vstack`,
57
+ - **CSS-only — CSS is the behavior.** Layout primitives: `m-vstack`,
56
58
  `m-hstack`, `m-zstack`, `m-center`, `m-box`, `m-grid`, `m-sidecar`,
57
59
  `m-switcher`, `m-reel`. Zero JS, work with JS disabled.
58
- - **Tier 1 — the browser is the behavior.** Styled native elements:
60
+ - **Native behavior — the browser is the behavior.** Styled native elements:
59
61
  buttons, forms, `<dialog>`, `<details>` accordions, popover menus,
60
62
  tooltips, toasts. Delete the stylesheet and everything still works.
61
- - **Tier 2 — script, honestly.** Where accessibility genuinely requires
63
+ - **JS-enhanced — script, honestly.** Where accessibility genuinely requires
62
64
  JS: `tabs.js`, `combobox.js`, `field.js` (declarative validation),
63
65
  `select.js`, `toast.js`. Each is a tiny standalone module that enhances
64
66
  working markup — never renders it. There is no shared runtime.
@@ -83,8 +85,8 @@ how. Nothing breaks; some things get plainer.
83
85
  mica itself (view source). The [demo](https://akonwi.io/mica/demo.html) is
84
86
  the whole library on one page.
85
87
 
86
- Read [VISION.md](VISION.md) for the philosophy and [TIERS.md](TIERS.md)
87
- for the tier system.
88
+ Read [VISION.md](VISION.md) for the philosophy and
89
+ [PROGRESSIVE.md](PROGRESSIVE.md) for how components are allowed to work.
88
90
 
89
91
  ## License
90
92
 
package/combobox.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ /* Type declarations for mica/combobox.js — side-effect module.
2
+ * Registers <m-combobox>: filterable input + listbox over native markup.
3
+ */
4
+ declare global {
5
+ interface HTMLElementTagNameMap {
6
+ "m-combobox": HTMLElement;
7
+ }
8
+ }
9
+ export {};
package/combobox.js CHANGED
@@ -1,4 +1,4 @@
1
- /* mica/combobox.js — <m-combobox>: filterable input + listbox. Tier 2.
1
+ /* mica/combobox.js — <m-combobox>: filterable input + listbox. JS-enhanced module.
2
2
  *
3
3
  * The no-JS state is a native <datalist> — real autocomplete, fully
4
4
  * functional, just unstylable. The module upgrades it into the ARIA
package/drawer.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ /* Type declarations for mica/drawer.js — side-effect module.
2
+ * Swipe-to-dismiss drag physics for dialog[data-drawer] bottom sheets.
3
+ * No exports; sets :root[data-drawer-gestures] when active.
4
+ */
5
+ export {};
package/drawer.js CHANGED
@@ -1,4 +1,4 @@
1
- /* mica/drawer.js — Tier 2: swipe-to-dismiss for dialog[data-drawer]
1
+ /* mica/drawer.js — JS-enhanced: swipe-to-dismiss for dialog[data-drawer]
2
2
  * bottom sheets on small screens.
3
3
  *
4
4
  * Enhances working markup, never replaces it: without this module the
package/field.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ /* Type declarations for mica/field.js — side-effect module.
2
+ * Registers <m-field>: declarative validation errors via <m-error match>.
3
+ */
4
+ declare global {
5
+ interface HTMLElementTagNameMap {
6
+ "m-field": HTMLElement;
7
+ "m-error": HTMLElement;
8
+ }
9
+ }
10
+ export {};
package/field.js CHANGED
@@ -1,4 +1,4 @@
1
- /* mica/field.js — <m-field>: declarative validation errors. Tier 2.
1
+ /* mica/field.js — <m-field>: declarative validation errors. JS-enhanced module.
2
2
  *
3
3
  * Enhances working light-DOM markup; never renders it. Without this
4
4
  * module, fields inside <m-field> fall back to native bubbles plus the
package/invoker.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ /* Type declarations for mica/invoker.js — side-effect module.
2
+ * Shims <button commandfor command> dialog commands where invoker
3
+ * commands aren't supported. No exports; installs nothing where native.
4
+ */
5
+ export {};
package/mica.css CHANGED
@@ -40,6 +40,13 @@
40
40
  --control-height: 2.25rem;
41
41
  --control-height-lg: 2.5rem;
42
42
 
43
+ /* Where toasts pin. App-level config — set once, all toasts obey:
44
+ bottom-right (default) | bottom-left | bottom-center |
45
+ top-right | top-left | top-center.
46
+ Read via container style queries (Baseline newly-available
47
+ 2026-05); browsers without them keep the bottom-right default. */
48
+ --toast-position: bottom-right;
49
+
43
50
  --focus-ring-width: 2px;
44
51
  --focus-ring-offset: 2px;
45
52
  --focus-ring-color: var(--color-accent);
@@ -267,7 +274,7 @@
267
274
  }
268
275
 
269
276
  /* ------------------------------------------------------------------ *
270
- * Elements — Tier 1. Native elements styled directly; behavior is the
277
+ * Elements — native behavior. Native elements styled directly; behavior is the
271
278
  * browser's. Presentation APIs are attributes; user CSS still wins
272
279
  * because these rules live in a layer.
273
280
  * ------------------------------------------------------------------ */
@@ -1184,7 +1191,7 @@
1184
1191
  }
1185
1192
 
1186
1193
  /* grab handle — only when drawer.js is loaded (an affordance
1187
- without its behavior would fake interactivity; TIERS.md).
1194
+ without its behavior would fake interactivity; PROGRESSIVE.md).
1188
1195
  the handle is the first flex item of the open sheet. */
1189
1196
  :where(:root[data-drawer-gestures])
1190
1197
  :where(dialog[data-drawer][open])::before {
@@ -1204,7 +1211,7 @@
1204
1211
  }
1205
1212
 
1206
1213
  /* scroll lock: the page must not scroll behind any modal dialog.
1207
- CSS-only best effort (→ no JS tier violation); iOS honors
1214
+ CSS-only best effort (→ no faked-behavior violation); iOS honors
1208
1215
  overflow:hidden on the root for modern versions. */
1209
1216
  :where(html):has(:where(dialog:modal)) {
1210
1217
  overflow: hidden;
@@ -1384,7 +1391,7 @@
1384
1391
  }
1385
1392
  }
1386
1393
 
1387
- /* --- combobox: m-combobox + combobox.js (Tier 2) ------------------ *
1394
+ /* --- combobox: m-combobox + combobox.js (JS-enhanced) ------------------ *
1388
1395
  * without the module: a native datalist — functional autocomplete.
1389
1396
  * with it: styled listbox, filtering, active-descendant keys.
1390
1397
  * positioned within the wrapper (no top layer) so it works everywhere.
@@ -1421,7 +1428,7 @@
1421
1428
  }
1422
1429
  }
1423
1430
 
1424
- /* --- tabs: m-tabs + tabs.js (Tier 2) ------------------------------ *
1431
+ /* --- tabs: m-tabs + tabs.js (JS-enhanced) ------------------------------ *
1425
1432
  * without the module: inert nav, all panels visible in order —
1426
1433
  * complete content. with it: tablist semantics + roving focus.
1427
1434
  * shadcn segmented look: muted track, surface active tab.
@@ -1481,7 +1488,7 @@
1481
1488
  padding-block: var(--space-md);
1482
1489
  }
1483
1490
 
1484
- /* --- toast: display only (queueing/auto-dismiss are Tier 2) ------- *
1491
+ /* --- toast: display only (queueing/auto-dismiss are JS-enhanced) ------- *
1485
1492
  * <m-toast popover="manual" role="status"> — manual so it survives
1486
1493
  * light dismiss and other popovers; role=status announces.
1487
1494
  * ------------------------------------------------------------------ */
@@ -1514,10 +1521,45 @@
1514
1521
  border-inline-start: 2px solid var(--color-warn);
1515
1522
  }
1516
1523
  }
1517
- /* restacking motion always (inset-block-end); the entrance slide/fade
1518
- only where overlay transitions work see the base popover note. */
1524
+ /* --toast-position overrides (base = bottom-right). Style queries read
1525
+ the token from the toast's parent; without support the default
1526
+ corner stands — graceful. Stack offset feeds whichever block inset
1527
+ is active; centers use inset-inline 0 + auto margins. */
1528
+ @container style(--toast-position: bottom-left) {
1529
+ :where(m-toast[popover]) {
1530
+ inset: auto auto calc(var(--space-lg) + var(--m-toast-offset, 0px)) var(--space-lg);
1531
+ }
1532
+ }
1533
+ @container style(--toast-position: bottom-center) {
1534
+ :where(m-toast[popover]) {
1535
+ inset: auto 0 calc(var(--space-lg) + var(--m-toast-offset, 0px)) 0;
1536
+ margin-inline: auto;
1537
+ }
1538
+ }
1539
+ @container style(--toast-position: top-right) {
1540
+ :where(m-toast[popover]) {
1541
+ inset: calc(var(--space-lg) + var(--m-toast-offset, 0px)) var(--space-lg) auto auto;
1542
+ }
1543
+ }
1544
+ @container style(--toast-position: top-left) {
1545
+ :where(m-toast[popover]) {
1546
+ inset: calc(var(--space-lg) + var(--m-toast-offset, 0px)) auto auto var(--space-lg);
1547
+ }
1548
+ }
1549
+ @container style(--toast-position: top-center) {
1550
+ :where(m-toast[popover]) {
1551
+ inset: calc(var(--space-lg) + var(--m-toast-offset, 0px)) 0 auto 0;
1552
+ margin-inline: auto;
1553
+ }
1554
+ }
1555
+
1556
+ /* restacking motion always (both block insets — only the active one
1557
+ ever changes); the entrance slide/fade only where overlay
1558
+ transitions work — see the base popover note. */
1519
1559
  :where(m-toast[popover]:popover-open) {
1520
- transition: inset-block-end 0.15s;
1560
+ transition:
1561
+ inset-block-end 0.15s,
1562
+ inset-block-start 0.15s;
1521
1563
  }
1522
1564
  @supports (overlay: auto) {
1523
1565
  :where(m-toast[popover]:popover-open) {
@@ -1525,6 +1567,7 @@
1525
1567
  opacity 0.1s,
1526
1568
  translate 0.1s,
1527
1569
  inset-block-end 0.15s,
1570
+ inset-block-start 0.15s,
1528
1571
  overlay 0.1s allow-discrete,
1529
1572
  display 0.1s allow-discrete;
1530
1573
  }
@@ -1533,6 +1576,17 @@
1533
1576
  translate: 0 0.75rem;
1534
1577
  }
1535
1578
  }
1579
+ /* top positions enter from above — must follow the base
1580
+ @starting-style in source order to win at equal specificity */
1581
+ @container style(--toast-position: top-right) or
1582
+ style(--toast-position: top-left) or
1583
+ style(--toast-position: top-center) {
1584
+ @starting-style {
1585
+ :where(m-toast[popover]:popover-open) {
1586
+ translate: 0 -0.75rem;
1587
+ }
1588
+ }
1589
+ }
1536
1590
  }
1537
1591
 
1538
1592
  /* --- tooltip: css-only, [data-tip] -------------------------------- *
@@ -1572,9 +1626,9 @@
1572
1626
  }
1573
1627
 
1574
1628
  /* --- validation errors ------------------------------------------- *
1575
- * Tier 1 (no JS): a generic <m-error> after a field shows when the
1629
+ * No JS: a generic <m-error> after a field shows when the
1576
1630
  * field is :user-invalid. Submit attempts fall back to native bubbles.
1577
- * Tier 2 (mica/field.js): <m-field> suppresses bubbles and activates
1631
+ * Enhanced (mica/field.js): <m-field> suppresses bubbles and activates
1578
1632
  * per-cause <m-error match="..."> messages. Rules below are all
1579
1633
  * zero-specificity; source order resolves them.
1580
1634
  * ------------------------------------------------------------------ */
@@ -1591,13 +1645,13 @@
1591
1645
  color: var(--color-danger-text);
1592
1646
  }
1593
1647
 
1594
- /* Tier-1 fallback: generic (matchless) error after user interaction */
1648
+ /* no-JS fallback: generic (matchless) error after user interaction */
1595
1649
  :where(input:user-invalid, textarea:user-invalid, select:user-invalid)
1596
1650
  ~ :where(m-error:not([match])) {
1597
1651
  display: block;
1598
1652
  }
1599
1653
 
1600
- /* Tier 2: once m-field upgrades, the module owns visibility */
1654
+ /* enhanced: once m-field upgrades, the module owns visibility */
1601
1655
  :where(m-field:defined) :where(m-error) {
1602
1656
  display: none;
1603
1657
  }
@@ -1642,7 +1696,7 @@
1642
1696
  }
1643
1697
 
1644
1698
  /* ------------------------------------------------------------------ *
1645
- * Layout primitives (Tier 0)
1699
+ * Layout primitives (CSS-only)
1646
1700
  *
1647
1701
  * Mechanism: attributes only set custom properties; rules read custom
1648
1702
  * properties. `gap="lg"` and `style="--gap: 2.5rem"` are the same thing.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akonwi/mica",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Custom elements. Native behavior. Nearly no JavaScript.",
5
5
  "keywords": [
6
6
  "css",
@@ -21,13 +21,13 @@
21
21
  "exports": {
22
22
  ".": "./mica.css",
23
23
  "./mica.css": "./mica.css",
24
- "./invoker.js": "./invoker.js",
25
- "./drawer.js": "./drawer.js",
26
- "./field.js": "./field.js",
27
- "./select.js": "./select.js",
28
- "./tabs.js": "./tabs.js",
29
- "./toast.js": "./toast.js",
30
- "./combobox.js": "./combobox.js",
24
+ "./invoker.js": { "types": "./invoker.d.ts", "default": "./invoker.js" },
25
+ "./drawer.js": { "types": "./drawer.d.ts", "default": "./drawer.js" },
26
+ "./field.js": { "types": "./field.d.ts", "default": "./field.js" },
27
+ "./select.js": { "types": "./select.d.ts", "default": "./select.js" },
28
+ "./tabs.js": { "types": "./tabs.d.ts", "default": "./tabs.js" },
29
+ "./toast.js": { "types": "./toast.d.ts", "default": "./toast.js" },
30
+ "./combobox.js": { "types": "./combobox.d.ts", "default": "./combobox.js" },
31
31
  "./types/react": {
32
32
  "types": "./types/react.d.ts"
33
33
  }
@@ -35,12 +35,19 @@
35
35
  "files": [
36
36
  "mica.css",
37
37
  "invoker.js",
38
+ "invoker.d.ts",
38
39
  "drawer.js",
40
+ "drawer.d.ts",
39
41
  "field.js",
42
+ "field.d.ts",
40
43
  "select.js",
44
+ "select.d.ts",
41
45
  "tabs.js",
46
+ "tabs.d.ts",
42
47
  "toast.js",
48
+ "toast.d.ts",
43
49
  "combobox.js",
50
+ "combobox.d.ts",
44
51
  "types/react.d.ts"
45
52
  ],
46
53
  "sideEffects": true,
package/select.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ /* Type declarations for mica/select.js — side-effect module.
2
+ * Aligns the open select picker with the selected option
3
+ * (alignItemWithTrigger). No exports; supplies --m-sel-index to CSS.
4
+ */
5
+ export {};
package/select.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /* mica/select.js — align the open select picker so the selected option
2
2
  * overlays the trigger (macOS-native / Base UI `alignItemWithTrigger`
3
- * behavior). Tier 2, and about as small as a module can be:
3
+ * behavior). A JS-enhanced module, and about as small as a module can be:
4
4
  *
5
5
  * JS supplies exactly one datum — the selected index, as a custom
6
6
  * property. All geometry lives in mica.css (anchor positioning).
package/tabs.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ /* Type declarations for mica/tabs.js — side-effect module.
2
+ * Registers <m-tabs>: roving tabindex, arrow keys, aria-selected wiring.
3
+ */
4
+ declare global {
5
+ interface HTMLElementTagNameMap {
6
+ "m-tabs": HTMLElement;
7
+ }
8
+ }
9
+ export {};
package/tabs.js CHANGED
@@ -1,4 +1,4 @@
1
- /* mica/tabs.js — <m-tabs>: accessible tabs. Tier 2.
1
+ /* mica/tabs.js — <m-tabs>: accessible tabs. JS-enhanced module.
2
2
  *
3
3
  * Enhances working light-DOM markup; never renders it. Without this
4
4
  * module, the nav buttons are inert and every panel is visible in
package/toast.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ /* Type declarations for mica/toast.js.
2
+ * Importing registers <m-toast> (queueing, auto-dismiss, restacking) and
3
+ * exports the imperative spawner.
4
+ */
5
+ export interface ToastOptions {
6
+ description?: string;
7
+ variant?: "success" | "warning" | "danger";
8
+ /** Auto-dismiss delay in ms; string accepted as attribute passthrough. */
9
+ duration?: number | string;
10
+ }
11
+ /** Create, show, and return an <m-toast> element. */
12
+ export function toast(title: string, options?: ToastOptions): HTMLElement;
13
+ declare global {
14
+ interface HTMLElementTagNameMap {
15
+ "m-toast": HTMLElement;
16
+ }
17
+ }
package/toast.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /* mica/toast.js — toast stacking, auto-dismiss, and a spawn helper.
2
- * Tier 2.
2
+ * JS-enhanced module.
3
3
  *
4
- * Enhances the Tier-1 <m-toast> recipe (corner-pinned manual popovers).
4
+ * Enhances the no-JS <m-toast> recipe (corner-pinned manual popovers).
5
5
  * Declared toasts keep working without this module — it adds:
6
6
  *
7
7
  * - stacking: open toasts stack upward; JS ships one number per toast