ab-nextjs-components 0.1.11 → 0.1.12

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/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## 0.1.12 — 2026-10-16
2
+
3
+ * ♿ **AbInstallSnippet tabs pass AA in every state.** Each state now paints an explicit text/background pair from the live palette instead of a transparent tab and a 12% bronze tint: inactive `on-surface-variant` on `surface-container` (8.04:1 light / 9.67:1 dark), hover `on-surface` on an 8% `on-surface` state layer (12.63:1 / 10.39:1), active and active + hover `on-primary-container` on `primary-container` (13.28:1 / 7.22:1). Fallbacks are the light bronze palette, so it also passes without the theme
4
+ * 🥉 **New `AbButton type="filled"`** (client + server): solid bronze `--md-sys-color-primary` with `on-primary` label and icon. Sets `data-type="filled"`, styled with hover / focus / disabled by `ab-nextjs-theme@^0.3.3`; a `.isFilled` fallback paints the rest state on older themes. `AbButtonType` gains `'filled'`
5
+ * ⬆️ **`ab-nextjs-theme ^0.3.3`** (peer + dev, was `^0.3.1`) for the filled hover / focus / disabled states and the button icon-colour fix. The `.isFilled` fallback stays for apps still on an older theme
6
+ * Tests: contrast computed from the theme palette (light + dark, every state, plus the no-theme fallbacks); filled type on client + server twins. Checked in WebKit (ratios above; filled 6.47:1 light / 7.74:1 dark)
7
+
1
8
  ## 0.1.11 — 2026-10-15
2
9
 
3
10
  * 🐛 **AbFlowDiagram: nodes no longer cover the header in small boxes.** At 390px wide the Live Playground's top node ("core") sat under the "Live Playground" header. With a header (title, live dot or caption) the root now gets `data-ab-header="true"`, and nodes + wires live in a stage that starts below the header band: `--ab-flow-diagram-inset-top` (default `2.75rem`, tunable). Node `x` / `y` stay 0–100 but now map onto that stage, and the wires share the same box so they still meet the nodes. Without a header the stage is the full card (unchanged)
package/README.md CHANGED
@@ -66,6 +66,14 @@ Shipped assets: `assets/logos/ab-logo.svg`, `assets/spinners/3-dots-scale.svg`,
66
66
  * `<AbLogo>` defaults to the Ab logo **shipped in this package** (data URI / `ab-logo/ab-logo.svg`). Pass `src` to override.
67
67
 
68
68
 
69
+ ### Buttons & install snippet (0.1.12)
70
+
71
+ * `<AbButton type="filled">` 🥉 is a solid bronze button (`--md-sys-color-primary` background, `--md-sys-color-on-primary` label and icon). It sets `data-type="filled"`, which **`ab-nextjs-theme@^0.3.3`** styles with hover, focus and disabled states. On older themes a built-in fallback still paints the solid bronze rest state.
72
+ ```tsx
73
+ <AbButton type="filled" label="Get started" />
74
+ ```
75
+ * `<AbInstallSnippet>` tabs are **≥ 4.5:1** (WCAG AA) in light and dark, active, inactive and hover: inactive = on-surface-variant on surface-container, hover = on-surface on an 8% state layer, active = on-primary-container on primary-container.
76
+
69
77
  ## Getting Started
70
78
 
71
79
  ### Installation
@@ -37,7 +37,9 @@
37
37
  */
38
38
 
39
39
  // 🎨 visual types shared by the client + server twins
40
- export type AbButtonType = 'contained' | 'outlined' | 'text';
40
+ // 🥉 'filled' (0.1.12): solid bronze, painted by ab-nextjs-theme ≥ 0.3.3's
41
+ // `button[data-type="filled"]` rule (fallback in styles.module.css → .isFilled)
42
+ export type AbButtonType = 'contained' | 'filled' | 'outlined' | 'text';
41
43
  // 🎨 colour roles
42
44
  export type AbButtonColor = 'primary' | 'secondary' | 'tertiary' | 'white' | 'black';
43
45
  // 🏷️ badge tones (G22): neutral grey or bronze accent
@@ -63,7 +63,7 @@ export type { AbButtonBadgeTone, AbButtonColor, AbButtonType, AbButtonDisabledMo
63
63
  // ⚙️ props
64
64
  export interface AbButtonProps {
65
65
  id?: string; // 🆔
66
- type?: AbButtonType; // 🎨 contained / outlined / text
66
+ type?: AbButtonType; // 🎨 contained / filled 🥉 / outlined / text
67
67
  htmlType?: 'button' | 'submit' | 'reset'; // 🧾
68
68
  className?: string; // 🏷️
69
69
  spinnerClassName?: string; // ⏳ spinner class
@@ -140,6 +140,7 @@ const AbButton = forwardRef<HTMLButtonElement | HTMLAnchorElement, AbButtonProps
140
140
  className, // 🏷️
141
141
  rounded && styles.abButtonRounded, // 💊
142
142
  type === 'contained' && styles.isContained, // 🎨
143
+ type === 'filled' && styles.isFilled, // 🥉 solid bronze (theme ≥ 0.3.3 paints it, this is the fallback)
143
144
  mode === 'native' && styles.isDisabled, // 🚫
144
145
  );
145
146
 
@@ -62,6 +62,17 @@
62
62
 
63
63
  .isContained {}
64
64
 
65
+ /* 🥉 Filled (0.1.12): solid bronze fallback for apps on ab-nextjs-theme < 0.3.3.
66
+ * Theme ≥ 0.3.3 ships `button[data-type="filled"]` (higher specificity), which
67
+ * adds the hover / focus / disabled state layers on top of this 🎨 */
68
+ .isFilled {
69
+ --material-icon-color: var(--ab-button-text-color, var(--md-sys-color-on-primary, #fff)); /* 🎨 icon = label */
70
+
71
+ background: var(--ab-button-background-color, var(--md-sys-color-primary, rgb(133 83 24)));
72
+ color: var(--ab-button-text-color, var(--md-sys-color-on-primary, #fff));
73
+ font-weight: bold;
74
+ }
75
+
65
76
  .isDisabled {
66
77
  pointer-events: none;
67
78
  opacity: 0.6;
@@ -53,21 +53,31 @@
53
53
  gap: 0.25rem; /* 🧱 */
54
54
  }
55
55
 
56
- /* 🔘 tab */
56
+ /* 🔘 tab
57
+ * ♿ 0.1.12: every state is an explicit text/background pair from the live
58
+ * Material palette, so it is ≥ 4.5:1 in light AND dark (tested in
59
+ * __tests__/ab-install-snippet-contrast.test.ts). Fallbacks are the light
60
+ * bronze palette, for apps that don't load the theme 🎨 */
57
61
  .tab {
58
62
  min-height: 28px; /* 📏 ≥ 24px target */
59
63
  padding: 0.25rem 0.625rem; /* 🧱 */
60
64
  border: 0; /* 🧹 */
61
65
  border-radius: 9999px; /* 💊 */
62
- background: transparent; /* 🫥 */
63
- color: var(--ab-color-on-surface-variant, currentColor); /* 🎨 */
66
+ background: var(--ab-color-surface-container, var(--md-sys-color-surface-container, rgb(252 235 226))); /* 🫥 inactive = card surface */
67
+ color: var(--ab-color-on-surface-variant, var(--md-sys-color-on-surface-variant, rgb(80 69 58))); /* 🎨 inactive label */
64
68
  font: inherit; /* 🔤 */
65
69
  font-size: 0.8125rem; /* 🔡 */
66
70
  cursor: pointer; /* 👆 */
67
71
  }
68
- .tab[aria-selected='true'] {
69
- color: var(--ab-color-bronze-text, #8A6440); /* ♿ AA small text */
70
- background: color-mix(in srgb, var(--ab-color-bronze, #A67C52) 12%, transparent); /* 🎨 */
72
+ /* 🖱️ hover (inactive): 8% on-surface state layer + full on-surface label */
73
+ .tab:hover {
74
+ background: color-mix(in srgb, var(--ab-color-on-surface, var(--md-sys-color-on-surface, rgb(34 26 21))) 8%, var(--ab-color-surface-container, var(--md-sys-color-surface-container, rgb(252 235 226))));
75
+ color: var(--ab-color-on-surface, var(--md-sys-color-on-surface, rgb(34 26 21)));
76
+ }
77
+ /* ✅ active (and active + hover): solid bronze container + its on-colour */
78
+ .tab[aria-selected='true'], .tab[aria-selected='true']:hover {
79
+ background: var(--md-sys-color-primary-container, rgb(255 220 189)); /* 🥉 */
80
+ color: var(--md-sys-color-on-primary-container, rgb(44 22 0)); /* ♿ */
71
81
  font-weight: 600; /* 💪 */
72
82
  }
73
83
  .tab:focus-visible {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ab-nextjs-components",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "🚀 A lightweight collection of server & client React components created by Abraham Ukachi, and optimized for Next.js applications 😎.",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -43,7 +43,7 @@
43
43
  "ab-nextjs-animations": "^0.2.4",
44
44
  "ab-nextjs-hooks": "^0.1.6",
45
45
  "ab-nextjs-icons": "^0.2.3",
46
- "ab-nextjs-theme": "^0.3.1",
46
+ "ab-nextjs-theme": "^0.3.3",
47
47
  "clsx": "^2",
48
48
  "next": "^16.3.4",
49
49
  "react": "^19",
@@ -56,7 +56,7 @@
56
56
  "ab-nextjs-animations": "^0.2.4",
57
57
  "ab-nextjs-hooks": "^0.1.6",
58
58
  "ab-nextjs-icons": "^0.2.3",
59
- "ab-nextjs-theme": "^0.3.1",
59
+ "ab-nextjs-theme": "^0.3.3",
60
60
  "clsx": "^2.1.1",
61
61
  "esbuild": "^0.28.2",
62
62
  "eslint": "^9.39.0",
@@ -94,7 +94,7 @@ const AbButton = ({
94
94
  const srReason = getAbButtonSrReason({ disabled, disabledReason });
95
95
 
96
96
  // 🏷️ classes
97
- const classNames = clsx('AbButton', styles.abButton, className, rounded && styles.abButtonRounded, mode === 'native' && styles.isDisabled);
97
+ const classNames = clsx('AbButton', styles.abButton, className, rounded && styles.abButtonRounded, type === 'filled' && styles.isFilled /* 🥉 */, mode === 'native' && styles.isDisabled);
98
98
 
99
99
  // 🗂️ data attributes
100
100
  const dataAttrs = {
@@ -62,6 +62,17 @@
62
62
 
63
63
  .isContained {}
64
64
 
65
+ /* 🥉 Filled (0.1.12): solid bronze fallback for apps on ab-nextjs-theme < 0.3.3.
66
+ * Theme ≥ 0.3.3 ships `button[data-type="filled"]` (higher specificity), which
67
+ * adds the hover / focus / disabled state layers on top of this 🎨 */
68
+ .isFilled {
69
+ --material-icon-color: var(--ab-button-text-color, var(--md-sys-color-on-primary, #fff)); /* 🎨 icon = label */
70
+
71
+ background: var(--ab-button-background-color, var(--md-sys-color-primary, rgb(133 83 24)));
72
+ color: var(--ab-button-text-color, var(--md-sys-color-on-primary, #fff));
73
+ font-weight: bold;
74
+ }
75
+
65
76
  .isDisabled {
66
77
  pointer-events: none;
67
78
  opacity: 0.6;