@akonwi/mica 0.2.1 → 0.3.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/mica.css CHANGED
@@ -644,16 +644,9 @@
644
644
  border-inline-start: 1px solid var(--color-border-strong);
645
645
  }
646
646
 
647
- :where(m-segmented) > :where(label) > :where(input[type="radio"]) {
648
- position: absolute;
649
- inline-size: 1px;
650
- block-size: 1px;
651
- margin: -1px;
652
- padding: 0;
653
- overflow: hidden;
654
- clip-path: inset(50%);
655
- white-space: nowrap;
656
- }
647
+ /* The radio itself is hidden but focusable and announced — the label
648
+ is the visible control. Shares the one hiding rule at the end of
649
+ this layer (see "visually hidden"). */
657
650
 
658
651
  :where(m-segmented) > :where(label):has(> input:checked) {
659
652
  background: var(--color-primary);
@@ -1611,6 +1604,41 @@
1611
1604
  :where(m-error[active]) {
1612
1605
  display: block;
1613
1606
  }
1607
+
1608
+ /* --- visually hidden: off-screen, still announced ------------------ *
1609
+ *
1610
+ * The one utility mica ships, and an accessibility primitive rather
1611
+ * than a style: content that assistive tech must reach but sighted
1612
+ * users must not see. `display: none` and `inline-size: 0` would drop
1613
+ * it from the accessibility tree; this keeps a 1px box in flow-ish
1614
+ * and clips it away.
1615
+ *
1616
+ * An attribute, not a class: `class` stays the app's, `data-*` is
1617
+ * valid HTML and needs no React `HTMLAttributes` augmentation, and
1618
+ * `.visually-hidden`/`.sr-only` are the most copy-pasted names in
1619
+ * accessibility — a consumer arriving with their own copy is the
1620
+ * likely case. Lives at the end of this layer, not a new one, because
1621
+ * the layer list is a published contract; being last lets it beat the
1622
+ * element rules it is applied to (`caption`, `legend`, headings).
1623
+ *
1624
+ * <caption data-visually-hidden>Standings</caption>
1625
+ * <a href="#main" data-visually-hidden="focusable">Skip to content</a>
1626
+ *
1627
+ * The `focusable` variant is the skip-link primitive: hidden until it
1628
+ * takes focus, then it simply stops being hidden — no undo rule, so
1629
+ * the element keeps whatever styling the app gave it. */
1630
+ [data-visually-hidden]:not([data-visually-hidden="focusable"]),
1631
+ [data-visually-hidden="focusable"]:not(:focus, :focus-within),
1632
+ :where(m-segmented) > :where(label) > :where(input[type="radio"]) {
1633
+ position: absolute;
1634
+ inline-size: 1px;
1635
+ block-size: 1px;
1636
+ margin: -1px;
1637
+ padding: 0;
1638
+ overflow: hidden;
1639
+ clip-path: inset(50%);
1640
+ white-space: nowrap;
1641
+ }
1614
1642
  }
1615
1643
 
1616
1644
  /* ------------------------------------------------------------------ *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akonwi/mica",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Custom elements. Native behavior. Nearly no JavaScript.",
5
5
  "keywords": [
6
6
  "css",
@@ -27,7 +27,10 @@
27
27
  "./select.js": "./select.js",
28
28
  "./tabs.js": "./tabs.js",
29
29
  "./toast.js": "./toast.js",
30
- "./combobox.js": "./combobox.js"
30
+ "./combobox.js": "./combobox.js",
31
+ "./types/react": {
32
+ "types": "./types/react.d.ts"
33
+ }
31
34
  },
32
35
  "files": [
33
36
  "mica.css",
@@ -37,7 +40,8 @@
37
40
  "select.js",
38
41
  "tabs.js",
39
42
  "toast.js",
40
- "combobox.js"
43
+ "combobox.js",
44
+ "types/react.d.ts"
41
45
  ],
42
46
  "sideEffects": true,
43
47
  "devDependencies": {
@@ -51,4 +55,4 @@
51
55
  "snapshot:check": "bun tools/snapshot.ts --check",
52
56
  "snapshot:setup": "playwright install chromium webkit"
53
57
  }
54
- }
58
+ }
@@ -0,0 +1,64 @@
1
+ // React JSX typings for mica's custom elements.
2
+ //
3
+ // React 19 supports custom elements natively; this file only teaches
4
+ // TypeScript the tags and their styling attributes. It is the typed
5
+ // mirror of the attribute selectors in mica.css — when an element or
6
+ // attribute changes there, it changes here.
7
+ //
8
+ // Usage (tsconfig `include` or a triple-slash reference):
9
+ // /// <reference types="@akonwi/mica/types/react" />
10
+ // or copy the file alongside a vendored mica.css.
11
+
12
+ import type { DetailedHTMLProps, HTMLAttributes } from 'react'
13
+
14
+ type Gap = 'none' | '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
15
+ type Align = 'start' | 'center' | 'end' | 'stretch'
16
+ type Justify = 'start' | 'center' | 'end' | 'between' | 'stretch'
17
+ type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
18
+
19
+ type MicaElement<Extra = Record<never, never>> = DetailedHTMLProps<
20
+ HTMLAttributes<HTMLElement>,
21
+ HTMLElement
22
+ > &
23
+ Extra
24
+
25
+ type StackProps = {
26
+ gap?: Gap
27
+ align?: Align
28
+ justify?: Justify
29
+ }
30
+
31
+ declare module 'react' {
32
+ namespace JSX {
33
+ interface IntrinsicElements {
34
+ // layout
35
+ 'm-vstack': MicaElement<StackProps>
36
+ 'm-hstack': MicaElement<StackProps & { wrap?: boolean }>
37
+ 'm-zstack': MicaElement
38
+ 'm-center': MicaElement
39
+ 'm-box': MicaElement
40
+ 'm-grid': MicaElement<{ min?: Breakpoint; gap?: Gap }>
41
+ 'm-sidecar': MicaElement<{ side?: 'start' | 'end'; gap?: Gap }>
42
+ 'm-switcher': MicaElement<{ threshold?: Breakpoint; gap?: Gap }>
43
+ 'm-reel': MicaElement<{ gap?: Gap }>
44
+ 'm-frame': MicaElement<{
45
+ ratio?: 'square' | '16:9' | '9:16' | '4:3' | '3:2' | '2:1'
46
+ }>
47
+ 'm-cover': MicaElement<{
48
+ gap?: Gap
49
+ pad?: 'none' | 'sm' | 'md' | 'lg' | 'xl'
50
+ }>
51
+
52
+ // components
53
+ 'm-segmented': MicaElement
54
+ 'm-tabs': MicaElement
55
+ 'm-badge': MicaElement<{
56
+ variant?: 'primary' | 'success' | 'warning' | 'danger'
57
+ count?: boolean
58
+ }>
59
+ 'm-field': MicaElement
60
+ 'm-error': MicaElement<{ active?: boolean }>
61
+ 'm-combobox': MicaElement
62
+ }
63
+ }
64
+ }