@jirawatpyk/aura-tokens 4.15.0 → 4.16.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/GOVERNANCE.md CHANGED
@@ -53,7 +53,7 @@ Made by the design-system owner in one pass, instead of separate Proposals. Each
53
53
  - **Form tokens added**: `aura-bg-input`, `aura-bg-input-disabled`, `aura-fg-danger`, `aura-border-danger`, `aura-border-control`, `aura-input-height`.
54
54
  - **Textures adopted** as Creative-only: mesh and 4% grain.
55
55
  - **Table tokens adopted**: `aura-bg-selected` (opaque in dark), `aura-bg-skeleton`, `aura-table-select-width`, `aura-control-sm`; the header uses `aura-fg-secondary` instead of 60% opacity.
56
- - **Fonts** stay on Google Fonts (no self-hosted files) until a team needs offline use.
56
+ - **Fonts** stay on Google Fonts (no self-hosted files) until a team needs offline use. (4.16: Chamber-OS needed it — its CSP allows only `font-src 'self'` — so the four families now also ship as woff2 files with `aura-fonts.local.css`; Google stays the default for `aura-fonts.css`.)
57
57
 
58
58
  ## Decisions (4.2)
59
59
 
package/README.md CHANGED
@@ -4,19 +4,20 @@ Enterprise Standard, Human Creative. Tokens, Tailwind preset, Figma variables a
4
4
 
5
5
  ## What's inside
6
6
 
7
- | File | What |
8
- |---|---|
9
- | `tokens.json` | **Source of truth.** 3-tier: primitive → semantic (light/dark) → component, plus spacing, radius, shadow (per theme), size, motion, opacity, z-index, typography. |
10
- | `aura.css` | Every token as a CSS variable. Light on `:root` / `[data-theme="light"]`, dark on `.dark` / `[data-theme="dark"]`. **Generated.** |
11
- | `aura-fonts.css` | The Google Fonts `@import` (Fraunces, Inter, JetBrains Mono, Noto Sans Thai). Kept separate so a network that blocks Google can't break `aura.css`. **Generated.** |
12
- | `tailwind.config.ts` + `tailwind.tokens.cjs` | Tailwind v3 theme wired to the variables (`bg-bg-surface`, `text-fg-danger`, `rounded-aura-xl`, `shadow-aura-overlay` …). The `.cjs` is **generated**. |
13
- | `aura-tailwind.css` | The same class names for Tailwind v4 (`@theme inline`, CSS-first). **Generated**; exported as `@jirawatpyk/aura-tokens/tailwind.css`. |
14
- | `figma-variables.csv` | 204 variables: all colours in both modes (aliases kept), spacing, radius and sizes. **Generated.** |
15
- | `components/` | `aura.bundle.js` (sets `window.Aura`), `aura.components.css`, `index.d.ts`. |
16
- | `eslint-plugin-aura.js` | `aura/no-hardcoded-color`: hex, rgb()/hsl() and Tailwind `bg-[#…]` classes. |
17
- | `scripts/build-tokens.js` | Rebuilds the generated files from `tokens.json`. |
18
- | `scripts/a11y-check.js` | Checks 94 text/ground pairs in both themes (WCAG AA). |
19
- | `scripts/lint-tokens.js` | Fails on hard-coded colours (a file that must hold them says `aura-lint: allow-colours` with a reason). |
7
+ | File | What |
8
+ | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
9
+ | `tokens.json` | **Source of truth.** 3-tier: primitive → semantic (light/dark) → component, plus spacing, radius, shadow (per theme), size, motion, opacity, z-index, typography. |
10
+ | `aura.css` | Every token as a CSS variable. Light on `:root` / `[data-theme="light"]`, dark on `.dark` / `[data-theme="dark"]`. **Generated.** |
11
+ | `aura-fonts.css` | The Google Fonts `@import` (Fraunces, Inter, JetBrains Mono, Noto Sans Thai). Kept separate so a network that blocks Google can't break `aura.css`. **Generated.** |
12
+ | `aura-fonts.local.css` + `fonts/` | The same four families self-hosted: `@font-face` rules over woff2 files in the package (4.16), for a `font-src 'self'` CSP. `npm run check:fonts` loads them under that CSP. **Generated.** |
13
+ | `tailwind.config.ts` + `tailwind.tokens.cjs` | Tailwind v3 theme wired to the variables (`bg-bg-surface`, `text-fg-danger`, `rounded-aura-xl`, `shadow-aura-overlay` …). The `.cjs` is **generated**. |
14
+ | `aura-tailwind.css` | The same class names for Tailwind v4 (`@theme inline`, CSS-first). **Generated**; exported as `@jirawatpyk/aura-tokens/tailwind.css`. |
15
+ | `figma-variables.csv` | 204 variables: all colours in both modes (aliases kept), spacing, radius and sizes. **Generated.** |
16
+ | `components/` | `aura.bundle.js` (sets `window.Aura`), `aura.components.css`, `index.d.ts`. |
17
+ | `eslint-plugin-aura.js` | `aura/no-hardcoded-color`: hex, rgb()/hsl() and Tailwind `bg-[#…]` classes. |
18
+ | `scripts/build-tokens.js` | Rebuilds the generated files from `tokens.json`. |
19
+ | `scripts/a11y-check.js` | Checks 94 text/ground pairs in both themes (WCAG AA). |
20
+ | `scripts/lint-tokens.js` | Fails on hard-coded colours (a file that must hold them says `aura-lint: allow-colours` with a reason). |
20
21
 
21
22
  ## ใช้งาน (5 นาที)
22
23
 
@@ -28,16 +29,49 @@ npm install @aura/tokens@npm:@jirawatpyk/aura-tokens @aura/react@npm:@jirawatpyk
28
29
 
29
30
  ```tsx
30
31
  // app/layout.tsx
31
- import '@aura/tokens/aura-fonts.css' // Google Fonts (or use the <link> tags below instead)
32
- import '@aura/tokens/aura.css' // tokens
33
- import '@aura/react/styles.css' // component styles (with @aura/react)
32
+ import '@aura/tokens/aura-fonts.css'; // Google Fonts (or use the <link> tags below instead)
33
+ import '@aura/tokens/aura.css'; // tokens
34
+ import '@aura/react/styles.css'; // component styles (with @aura/react)
34
35
  ```
35
36
 
36
37
  ```html
37
38
  <!-- Preferred: load the fonts from <head> instead of aura-fonts.css — faster, and a blocked host fails quietly -->
38
- <link rel="preconnect" href="https://fonts.googleapis.com">
39
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
40
- <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,600&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400&family=Noto+Sans+Thai:wght@400;500;600&display=swap">
39
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
40
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
41
+ <link
42
+ rel="stylesheet"
43
+ href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,600&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400&family=Noto+Sans+Thai:wght@400;500;600&display=swap"
44
+ />
45
+ ```
46
+
47
+ ### Fonts
48
+
49
+ Pick one of three ways to load Fraunces, Inter, JetBrains Mono and Noto Sans Thai:
50
+
51
+ | Option | Use when | How |
52
+ | ------------------ | --------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
53
+ | Self-hosted (4.16) | Your CSP allows only `font-src 'self'`, or you want no third-party requests | `import '@jirawatpyk/aura-tokens/aura-fonts.local.css'` — woff2 files ship in the package (`fonts/`, OFL-1.1), split by unicode range so a page downloads only what it uses |
54
+ | `next/font` | Next.js apps that want preloading and zero layout shift | Load the families with `next/font` and point the AURA font variables at them (below) |
55
+ | Google Fonts | Anything else | The `<link>` tags below, or `aura-fonts.css` |
56
+
57
+ ```tsx
58
+ // app/layout.tsx — next/font (downloads at build time and self-hosts, so font-src 'self' is enough)
59
+ import { Fraunces, Inter, JetBrains_Mono, Noto_Sans_Thai } from 'next/font/google';
60
+ const display = Fraunces({ subsets: ['latin', 'latin-ext'], axes: ['opsz'], variable: '--next-display' });
61
+ const sans = Inter({ subsets: ['latin', 'latin-ext'], variable: '--next-sans' });
62
+ const mono = JetBrains_Mono({ subsets: ['latin', 'latin-ext'], weight: '400', variable: '--next-mono' });
63
+ const thai = Noto_Sans_Thai({ subsets: ['thai'], variable: '--next-thai' });
64
+ // <html className={[display, sans, mono, thai].map((f) => f.variable).join(' ')}>
65
+ ```
66
+
67
+ ```css
68
+ /* app/globals.css, after aura.css — AURA's stacks now use the next/font families */
69
+ :root {
70
+ --font-display: var(--next-display), var(--next-thai), serif;
71
+ --font-sans: var(--next-sans), var(--next-thai), sans-serif;
72
+ --font-mono: var(--next-mono), monospace;
73
+ --font-thai: var(--next-thai), sans-serif;
74
+ }
41
75
  ```
42
76
 
43
77
  Dark theme: put `class="dark"` (or `data-theme="dark"`) on `<html>`.
@@ -46,8 +80,8 @@ Dark theme: put `class="dark"` (or `data-theme="dark"`) on `<html>`.
46
80
 
47
81
  ```css
48
82
  /* app/globals.css */
49
- @import "tailwindcss";
50
- @import "@jirawatpyk/aura-tokens/tailwind.css"; /* imports aura.css too */
83
+ @import 'tailwindcss';
84
+ @import '@jirawatpyk/aura-tokens/tailwind.css'; /* imports aura.css too */
51
85
  ```
52
86
 
53
87
  Classes: `bg-bg-surface`, `text-fg-primary`, `border-border-strong`, `text-fg-danger`, `bg-alert-warning-bg`, `fill-chart-1`, `p-aura-6`, `gap-aura-4`, `rounded-aura-xl`, `shadow-aura-overlay`, `ease-aura-ease`, `duration-aura-fast`, `z-aura-dialog`, `font-display` / `font-sans` / `font-mono`. They point at the `--aura-*` variables, so `.dark` or `data-theme="dark"` switches them with no `dark:` prefix; the file also makes `dark:` follow those two switches. `npm run check:tailwind4` compiles it with Tailwind v4 in CI.
@@ -70,11 +104,12 @@ Classes: `bg-bg-surface`, `text-fg-primary`, `border-border-strong`, `text-fg-da
70
104
  The bundle is a classic script that reads `window.React` / `window.ReactDOM` and sets `window.Aura`:
71
105
 
72
106
  ```tsx
73
- import React from 'react'
74
- import ReactDOM from 'react-dom'
75
- ;(window as any).React = React; (window as any).ReactDOM = ReactDOM
76
- import '@aura/tokens/components.js'
77
- const { Button, TextField, DataTable, Dialog, toast, Toaster } = (window as any).Aura
107
+ import React from 'react';
108
+ import ReactDOM from 'react-dom';
109
+ (window as any).React = React;
110
+ (window as any).ReactDOM = ReactDOM;
111
+ import '@aura/tokens/components.js';
112
+ const { Button, TextField, DataTable, Dialog, toast, Toaster } = (window as any).Aura;
78
113
  ```
79
114
 
80
115
  Types for every component are in `components/index.d.ts`.
@@ -0,0 +1,58 @@
1
+ /* AURA fonts, self-hosted: Fraunces, Inter, JetBrains Mono and Noto Sans Thai from ./fonts (OFL-1.1, see fonts/LICENSE.md).
2
+ * Use instead of aura-fonts.css when your CSP allows only font-src 'self'. GENERATED. */
3
+ @font-face {
4
+ font-family: "Fraunces";
5
+ font-style: normal;
6
+ font-weight: 100 900;
7
+ font-display: swap;
8
+ src: url("./fonts/fraunces-latin-opsz-normal.woff2") format("woff2");
9
+ unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
10
+ }
11
+ @font-face {
12
+ font-family: "Fraunces";
13
+ font-style: normal;
14
+ font-weight: 100 900;
15
+ font-display: swap;
16
+ src: url("./fonts/fraunces-latin-ext-opsz-normal.woff2") format("woff2");
17
+ unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
18
+ }
19
+ @font-face {
20
+ font-family: "Inter";
21
+ font-style: normal;
22
+ font-weight: 100 900;
23
+ font-display: swap;
24
+ src: url("./fonts/inter-latin-wght-normal.woff2") format("woff2");
25
+ unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
26
+ }
27
+ @font-face {
28
+ font-family: "Inter";
29
+ font-style: normal;
30
+ font-weight: 100 900;
31
+ font-display: swap;
32
+ src: url("./fonts/inter-latin-ext-wght-normal.woff2") format("woff2");
33
+ unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
34
+ }
35
+ @font-face {
36
+ font-family: "JetBrains Mono";
37
+ font-style: normal;
38
+ font-weight: 400;
39
+ font-display: swap;
40
+ src: url("./fonts/jetbrains-mono-latin-400-normal.woff2") format("woff2");
41
+ unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
42
+ }
43
+ @font-face {
44
+ font-family: "JetBrains Mono";
45
+ font-style: normal;
46
+ font-weight: 400;
47
+ font-display: swap;
48
+ src: url("./fonts/jetbrains-mono-latin-ext-400-normal.woff2") format("woff2");
49
+ unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
50
+ }
51
+ @font-face {
52
+ font-family: "Noto Sans Thai";
53
+ font-style: normal;
54
+ font-weight: 100 900;
55
+ font-display: swap;
56
+ src: url("./fonts/noto-sans-thai-thai-wght-normal.woff2") format("woff2");
57
+ unicode-range: U+02D7,U+0303,U+0331,U+0E01-0E5B,U+200C-200D,U+25CC;
58
+ }
package/aura-tailwind.css CHANGED
@@ -1,4 +1,4 @@
1
- /* AURA v4.15.0 — Tailwind v4 theme. GENERATED by scripts/build-tokens.js from tokens.json. Do not edit by hand.
1
+ /* AURA v4.16.0 — Tailwind v4 theme. GENERATED by scripts/build-tokens.js from tokens.json. Do not edit by hand.
2
2
  * @import "tailwindcss";
3
3
  * @import "@jirawatpyk/aura-tokens/tailwind.css";
4
4
  * Gives bg-bg-surface, text-fg-primary, border-border-strong, p-aura-6, rounded-aura-xl, shadow-aura-overlay,
package/aura.css CHANGED
@@ -1,4 +1,4 @@
1
- /* AURA v4.15.0 — GENERATED by scripts/build-tokens.js from tokens.json. Do not edit by hand. */
1
+ /* AURA v4.16.0 — GENERATED by scripts/build-tokens.js from tokens.json. Do not edit by hand. */
2
2
  /* Fonts: load aura-fonts.css (or the <link> tags in README) separately, so a blocked Google Fonts never breaks this file. */
3
3
 
4
4
  :root, [data-theme="light"] {
@@ -293,6 +293,7 @@
293
293
  --aura-card-radius: 20px;
294
294
  --aura-button-radius: 9999px;
295
295
  --aura-button-height: 44px;
296
+ --aura-button-height-sm: 32px;
296
297
  --aura-icon-sm: 16px;
297
298
  --aura-icon-md: 20px;
298
299
  --aura-icon-lg: 24px;
@@ -661,7 +661,8 @@ window.Aura = (() => {
661
661
  "disabled",
662
662
  "linkComponent",
663
663
  "href",
664
- "fullWidth"
664
+ "fullWidth",
665
+ "size"
665
666
  ]);
666
667
  const Tag3 = disabled ? "a" : Link;
667
668
  return /* @__PURE__ */ React4.createElement(
@@ -673,7 +674,13 @@ window.Aura = (() => {
673
674
  role: disabled ? "link" : void 0,
674
675
  "aria-disabled": disabled || void 0,
675
676
  tabIndex: disabled ? -1 : props.tabIndex,
676
- className: cx("aura-btn", "aura-btn--" + variant, props.fullWidth && "aura-btn--full", props.className),
677
+ className: cx(
678
+ "aura-btn",
679
+ "aura-btn--" + variant,
680
+ props.size === "sm" && "aura-btn--sm",
681
+ props.fullWidth && "aura-btn--full",
682
+ props.className
683
+ ),
677
684
  onClick: disabled ? void 0 : props.onClick
678
685
  },
679
686
  props.icon ? /* @__PURE__ */ React4.createElement(Icon, { name: props.icon }) : null,
@@ -699,7 +706,8 @@ window.Aura = (() => {
699
706
  "iconRight",
700
707
  "loading",
701
708
  "onClick",
702
- "fullWidth"
709
+ "fullWidth",
710
+ "size"
703
711
  ]);
704
712
  return /* @__PURE__ */ React4.createElement(
705
713
  "button",
@@ -710,6 +718,7 @@ window.Aura = (() => {
710
718
  className: cx(
711
719
  "aura-btn",
712
720
  "aura-btn--" + variant,
721
+ props.size === "sm" && "aura-btn--sm",
713
722
  props.fullWidth && "aura-btn--full",
714
723
  loading && "is-loading",
715
724
  props.className
@@ -3097,6 +3106,7 @@ window.Aura = (() => {
3097
3106
  { key: "owner", label: "OWNER" }
3098
3107
  ];
3099
3108
  var SKELETON_WIDTHS = ["72%", "56%", "84%", "44%", "64%"];
3109
+ var STACK_WIDTHS = [360, 400, 480, 520, 560, 600, 640, 720, 768, 800, 900, 960, 1024];
3100
3110
  var ROW_H_DEFAULT = 48;
3101
3111
  var OVERSCAN = 8;
3102
3112
  var FLEX_MIN = 160;
@@ -3163,6 +3173,7 @@ window.Aura = (() => {
3163
3173
  const measure = !!props.stackBelow || columns.some(function(c) {
3164
3174
  return c.hideBelow != null;
3165
3175
  });
3176
+ const dual = !!props.stackBelow && boxWidth[0] == null && STACK_WIDTHS.indexOf(props.stackBelow) >= 0;
3166
3177
  React28.useEffect(
3167
3178
  function() {
3168
3179
  if (!measure || !wrapRef.current || typeof ResizeObserver === "undefined") return;
@@ -3174,9 +3185,15 @@ window.Aura = (() => {
3174
3185
  ro.disconnect();
3175
3186
  };
3176
3187
  },
3177
- [measure]
3188
+ [measure, dual]
3178
3189
  );
3179
3190
  const stacked = !!props.stackBelow && boxWidth[0] != null && boxWidth[0] < props.stackBelow;
3191
+ useIsoLayoutEffect(
3192
+ function() {
3193
+ if (measure && boxWidth[0] == null && wrapRef.current) boxWidth[1](wrapRef.current.getBoundingClientRect().width);
3194
+ },
3195
+ [measure]
3196
+ );
3180
3197
  function tooNarrow(c) {
3181
3198
  if (c.hideBelow == null || boxWidth[0] == null || stacked) return false;
3182
3199
  const px = typeof c.hideBelow === "number" ? c.hideBelow : BP[c.hideBelow];
@@ -3988,7 +4005,7 @@ window.Aura = (() => {
3988
4005
  } else if (height && rows.length && !loading) {
3989
4006
  foot = /* @__PURE__ */ React28.createElement("div", { className: "aura-table__foot" }, /* @__PURE__ */ React28.createElement("span", null, t.rowCount(shownTotal)), selectable && selected.length ? /* @__PURE__ */ React28.createElement("span", null, t.selectedCount(selected.length)) : /* @__PURE__ */ React28.createElement("span", null));
3990
4007
  }
3991
- if (stacked) {
4008
+ function renderStacked(wrapRefArg) {
3992
4009
  const titleCol = vis[0], pillCol = vis.filter(function(c) {
3993
4010
  return c.pill && c !== titleCol;
3994
4011
  })[0];
@@ -4052,7 +4069,7 @@ window.Aura = (() => {
4052
4069
  return /* @__PURE__ */ React28.createElement(
4053
4070
  "div",
4054
4071
  {
4055
- ref: wrapMerged,
4072
+ ref: wrapRefArg,
4056
4073
  "data-density": props.density,
4057
4074
  className: cx("aura-table aura-table--stacked", refreshing && "is-refreshing", props.className),
4058
4075
  role: "region",
@@ -4073,15 +4090,16 @@ window.Aura = (() => {
4073
4090
  foot
4074
4091
  );
4075
4092
  }
4093
+ if (stacked) return renderStacked(wrapMerged);
4076
4094
  const scrollStyle = {
4077
4095
  scrollPaddingLeft: "calc(var(--aura-space-6)" + selW + " + " + acc + "px)",
4078
4096
  scrollPaddingTop: ROW_H + "px"
4079
4097
  };
4080
4098
  if (height) scrollStyle.height = height + "px";
4081
- return /* @__PURE__ */ React28.createElement(
4099
+ const gridEl = /* @__PURE__ */ React28.createElement(
4082
4100
  "div",
4083
4101
  {
4084
- ref: wrapMerged,
4102
+ ref: dual ? void 0 : wrapMerged,
4085
4103
  "data-density": props.density,
4086
4104
  className: cx("aura-table", scrolledX[0] && "is-scrolled-x", refreshing && "is-refreshing", props.className)
4087
4105
  },
@@ -4137,6 +4155,8 @@ window.Aura = (() => {
4137
4155
  }
4138
4156
  ) : null
4139
4157
  );
4158
+ if (!dual) return gridEl;
4159
+ return /* @__PURE__ */ React28.createElement("div", { ref: wrapMerged, className: "aura-table-dual", "data-stack-below": props.stackBelow }, renderStacked(void 0), gridEl);
4140
4160
  });
4141
4161
 
4142
4162
  // src/Card.tsx
@@ -4598,26 +4618,25 @@ window.Aura = (() => {
4598
4618
  var AppShell = React38.forwardRef(function AppShell2(props, ref) {
4599
4619
  const t = useStrings();
4600
4620
  const bp = useBreakpoint();
4601
- const compact = bp === "base" || bp === "sm" || bp === "md";
4621
+ const wide = bp === "lg" || bp === "xl";
4602
4622
  const st = React38.useState(false), open = st[0], setOpen = st[1];
4603
4623
  React38.useEffect(
4604
4624
  function() {
4605
- if (!compact) setOpen(false);
4625
+ if (wide) setOpen(false);
4606
4626
  },
4607
- [compact]
4627
+ [wide]
4608
4628
  );
4609
- const navEl = props.nav;
4610
- const nav = props.nav && React38.isValidElement(props.nav) && compact ? React38.cloneElement(navEl, {
4629
+ const navEl = props.nav && React38.isValidElement(props.nav) ? props.nav : null;
4630
+ const drawerNav = navEl ? React38.cloneElement(navEl, {
4611
4631
  onChange: function(id) {
4612
4632
  if (navEl.props.onChange) navEl.props.onChange(id);
4613
4633
  setOpen(false);
4614
4634
  },
4615
4635
  className: cx(navEl.props.className, "is-in-drawer"),
4616
- /* The drawer is already full-size: never a rail, no collapse button. */
4617
4636
  collapsed: false,
4618
4637
  collapsible: false
4619
4638
  }) : props.nav;
4620
- return /* @__PURE__ */ React38.createElement("div", { ref, className: cx("aura-shell", compact && "is-compact", props.className) }, !compact ? /* @__PURE__ */ React38.createElement("div", { className: "aura-shell__nav" }, nav) : null, compact ? /* @__PURE__ */ React38.createElement(
4639
+ return /* @__PURE__ */ React38.createElement("div", { ref, className: cx("aura-shell", props.className) }, props.nav ? /* @__PURE__ */ React38.createElement("div", { className: "aura-shell__nav" }, props.nav) : null, props.nav ? /* @__PURE__ */ React38.createElement(
4621
4640
  Drawer,
4622
4641
  {
4623
4642
  open,
@@ -4629,10 +4648,11 @@ window.Aura = (() => {
4629
4648
  "aria-label": props.navLabel || t.navigation,
4630
4649
  dismissible: true
4631
4650
  },
4632
- nav
4633
- ) : null, /* @__PURE__ */ React38.createElement("div", { className: "aura-shell__main" }, props.header || compact ? /* @__PURE__ */ React38.createElement("header", { className: "aura-shell__bar" }, compact ? /* @__PURE__ */ React38.createElement(
4651
+ drawerNav
4652
+ ) : null, /* @__PURE__ */ React38.createElement("div", { className: "aura-shell__main" }, props.header || props.nav ? /* @__PURE__ */ React38.createElement("header", { className: cx("aura-shell__bar", !props.header && "aura-shell__bar--menu-only") }, props.nav ? /* @__PURE__ */ React38.createElement(
4634
4653
  IconButton,
4635
4654
  {
4655
+ className: "aura-shell__menu",
4636
4656
  icon: "menu",
4637
4657
  label: props.menuLabel || t.openNav,
4638
4658
  size: "md",
@@ -5668,7 +5688,10 @@ window.Aura = (() => {
5668
5688
  rows
5669
5689
  );
5670
5690
  }
5671
- const style = { width: props.width, height: props.height };
5691
+ const style = {
5692
+ width: props.width,
5693
+ height: props.height
5694
+ };
5672
5695
  if (v === "circle") {
5673
5696
  style.width = style.height = props.size || props.width || 40;
5674
5697
  }
@@ -49,6 +49,16 @@ a.aura-btn { text-decoration: none; }
49
49
  .aura-btn--secondary { border: 1px solid var(--aura-button-secondary-border); color: var(--aura-fg-primary); }
50
50
  .aura-btn--secondary:hover { background: var(--aura-bg-surface-hover); }
51
51
  /* Destructive: filled for the confirm step, outline beside other actions. Loading keeps the colour (busy, not unavailable). */
52
+ .aura-btn--ghost { color: var(--aura-fg-primary); }
53
+ .aura-btn--ghost:hover { background: var(--aura-bg-surface-hover); }
54
+ /* size="sm" (4.16): 32px for toolbars, table rows and card footers. On touch the pill stays 32px and a transparent
55
+ * ::after grows the hit area to 44px, as IconButton does. */
56
+ .aura-btn--sm { height: var(--aura-button-height-sm); padding: 0 var(--aura-space-3); gap: 6px; }
57
+ .aura-btn--sm.aura-btn--full { height: auto; min-height: var(--aura-button-height-sm); padding-block: var(--aura-space-1); }
58
+ @media (pointer: coarse) {
59
+ .aura-btn--sm { position: relative; }
60
+ .aura-btn--sm::after { content: ''; position: absolute; top: 50%; left: 50%; width: max(100%, var(--aura-touch-target)); height: max(100%, var(--aura-touch-target)); transform: translate(-50%, -50%); }
61
+ }
52
62
  .aura-btn--danger { background: var(--aura-button-danger-bg); color: var(--aura-button-danger-fg); }
53
63
  .aura-btn--danger:hover { background: var(--aura-button-danger-bg-hover); }
54
64
  .aura-btn--danger-secondary { border: 1px solid var(--aura-border-danger); color: var(--aura-fg-danger); background: transparent; }
@@ -809,7 +819,17 @@ a.aura-btn { text-decoration: none; }
809
819
  position: sticky; top: 0; z-index: 10; display: flex; align-items: center; gap: var(--aura-space-2);
810
820
  min-height: 56px; padding: 0 var(--aura-space-6); background: var(--aura-bg-surface); border-bottom: 1px solid var(--aura-border-default);
811
821
  }
812
- .aura-shell.is-compact .aura-shell__bar { padding: 0 var(--aura-space-2) 0 var(--aura-space-2); }
822
+ /* Responsive in CSS (4.16), so the server's HTML is already right on a phone: below lg the sidebar is hidden and the
823
+ * menu button shows; from lg up the reverse. JavaScript only opens and closes the drawer. */
824
+ .aura-shell__menu { display: inline-flex; }
825
+ @media (max-width: 1023.98px) {
826
+ .aura-shell__nav { display: none; }
827
+ .aura-shell__bar { padding: 0 var(--aura-space-2) 0 var(--aura-space-2); }
828
+ }
829
+ @media (min-width: 1024px) {
830
+ .aura-shell__menu { display: none; }
831
+ .aura-shell__bar--menu-only { display: none; }
832
+ }
813
833
  .aura-shell__bar-content { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--aura-space-3); }
814
834
  .aura-shell__content { flex: 1; min-width: 0; padding: var(--aura-space-6) var(--aura-space-4); }
815
835
  @media (min-width: 640px) { .aura-shell__content { padding: var(--aura-space-6); } }
@@ -1281,3 +1301,33 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1281
1301
  }
1282
1302
  .aura-density { display: contents; }
1283
1303
 
1304
+
1305
+ /* DataTable: cards before hydration (4.16). While a stackBelow table hasn't measured itself yet (server HTML, first
1306
+ * client render) it holds both layouts; these container queries show one, so a phone never sees the grid first. */
1307
+ .aura-table-dual { container-type: inline-size; }
1308
+ @container (min-width: 360px) { .aura-table-dual[data-stack-below='360'] > .aura-table--stacked { display: none; } }
1309
+ @container (max-width: 359.98px) { .aura-table-dual[data-stack-below='360'] > .aura-table:not(.aura-table--stacked) { display: none; } }
1310
+ @container (min-width: 400px) { .aura-table-dual[data-stack-below='400'] > .aura-table--stacked { display: none; } }
1311
+ @container (max-width: 399.98px) { .aura-table-dual[data-stack-below='400'] > .aura-table:not(.aura-table--stacked) { display: none; } }
1312
+ @container (min-width: 480px) { .aura-table-dual[data-stack-below='480'] > .aura-table--stacked { display: none; } }
1313
+ @container (max-width: 479.98px) { .aura-table-dual[data-stack-below='480'] > .aura-table:not(.aura-table--stacked) { display: none; } }
1314
+ @container (min-width: 520px) { .aura-table-dual[data-stack-below='520'] > .aura-table--stacked { display: none; } }
1315
+ @container (max-width: 519.98px) { .aura-table-dual[data-stack-below='520'] > .aura-table:not(.aura-table--stacked) { display: none; } }
1316
+ @container (min-width: 560px) { .aura-table-dual[data-stack-below='560'] > .aura-table--stacked { display: none; } }
1317
+ @container (max-width: 559.98px) { .aura-table-dual[data-stack-below='560'] > .aura-table:not(.aura-table--stacked) { display: none; } }
1318
+ @container (min-width: 600px) { .aura-table-dual[data-stack-below='600'] > .aura-table--stacked { display: none; } }
1319
+ @container (max-width: 599.98px) { .aura-table-dual[data-stack-below='600'] > .aura-table:not(.aura-table--stacked) { display: none; } }
1320
+ @container (min-width: 640px) { .aura-table-dual[data-stack-below='640'] > .aura-table--stacked { display: none; } }
1321
+ @container (max-width: 639.98px) { .aura-table-dual[data-stack-below='640'] > .aura-table:not(.aura-table--stacked) { display: none; } }
1322
+ @container (min-width: 720px) { .aura-table-dual[data-stack-below='720'] > .aura-table--stacked { display: none; } }
1323
+ @container (max-width: 719.98px) { .aura-table-dual[data-stack-below='720'] > .aura-table:not(.aura-table--stacked) { display: none; } }
1324
+ @container (min-width: 768px) { .aura-table-dual[data-stack-below='768'] > .aura-table--stacked { display: none; } }
1325
+ @container (max-width: 767.98px) { .aura-table-dual[data-stack-below='768'] > .aura-table:not(.aura-table--stacked) { display: none; } }
1326
+ @container (min-width: 800px) { .aura-table-dual[data-stack-below='800'] > .aura-table--stacked { display: none; } }
1327
+ @container (max-width: 799.98px) { .aura-table-dual[data-stack-below='800'] > .aura-table:not(.aura-table--stacked) { display: none; } }
1328
+ @container (min-width: 900px) { .aura-table-dual[data-stack-below='900'] > .aura-table--stacked { display: none; } }
1329
+ @container (max-width: 899.98px) { .aura-table-dual[data-stack-below='900'] > .aura-table:not(.aura-table--stacked) { display: none; } }
1330
+ @container (min-width: 960px) { .aura-table-dual[data-stack-below='960'] > .aura-table--stacked { display: none; } }
1331
+ @container (max-width: 959.98px) { .aura-table-dual[data-stack-below='960'] > .aura-table:not(.aura-table--stacked) { display: none; } }
1332
+ @container (min-width: 1024px) { .aura-table-dual[data-stack-below='1024'] > .aura-table--stacked { display: none; } }
1333
+ @container (max-width: 1023.98px) { .aura-table-dual[data-stack-below='1024'] > .aura-table:not(.aura-table--stacked) { display: none; } }