@danieljoffe/shared-ui 0.0.1

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 (116) hide show
  1. package/LICENSE.md +117 -0
  2. package/README.md +373 -0
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +5360 -0
  6. package/dist/lib/Alert.d.ts +13 -0
  7. package/dist/lib/Alert.d.ts.map +1 -0
  8. package/dist/lib/AspectRatio.d.ts +8 -0
  9. package/dist/lib/AspectRatio.d.ts.map +1 -0
  10. package/dist/lib/Avatar.d.ts +11 -0
  11. package/dist/lib/Avatar.d.ts.map +1 -0
  12. package/dist/lib/Badge.d.ts +12 -0
  13. package/dist/lib/Badge.d.ts.map +1 -0
  14. package/dist/lib/Breadcrumb.d.ts +12 -0
  15. package/dist/lib/Breadcrumb.d.ts.map +1 -0
  16. package/dist/lib/Button.d.ts +23 -0
  17. package/dist/lib/Button.d.ts.map +1 -0
  18. package/dist/lib/CTACard.d.ts +10 -0
  19. package/dist/lib/CTACard.d.ts.map +1 -0
  20. package/dist/lib/Card.d.ts +39 -0
  21. package/dist/lib/Card.d.ts.map +1 -0
  22. package/dist/lib/Checkbox.d.ts +8 -0
  23. package/dist/lib/Checkbox.d.ts.map +1 -0
  24. package/dist/lib/Container.d.ts +10 -0
  25. package/dist/lib/Container.d.ts.map +1 -0
  26. package/dist/lib/Divider.d.ts +11 -0
  27. package/dist/lib/Divider.d.ts.map +1 -0
  28. package/dist/lib/Dropdown.d.ts +17 -0
  29. package/dist/lib/Dropdown.d.ts.map +1 -0
  30. package/dist/lib/FormFieldError.d.ts +6 -0
  31. package/dist/lib/FormFieldError.d.ts.map +1 -0
  32. package/dist/lib/Grid.d.ts +26 -0
  33. package/dist/lib/Grid.d.ts.map +1 -0
  34. package/dist/lib/GridBg.d.ts +7 -0
  35. package/dist/lib/GridBg.d.ts.map +1 -0
  36. package/dist/lib/Heading.d.ts +12 -0
  37. package/dist/lib/Heading.d.ts.map +1 -0
  38. package/dist/lib/Input.d.ts +12 -0
  39. package/dist/lib/Input.d.ts.map +1 -0
  40. package/dist/lib/Kbd.d.ts +8 -0
  41. package/dist/lib/Kbd.d.ts.map +1 -0
  42. package/dist/lib/Loading.d.ts +12 -0
  43. package/dist/lib/Loading.d.ts.map +1 -0
  44. package/dist/lib/Modal.d.ts +18 -0
  45. package/dist/lib/Modal.d.ts.map +1 -0
  46. package/dist/lib/PageContainer.d.ts +22 -0
  47. package/dist/lib/PageContainer.d.ts.map +1 -0
  48. package/dist/lib/PageLayout.d.ts +14 -0
  49. package/dist/lib/PageLayout.d.ts.map +1 -0
  50. package/dist/lib/Pagination.d.ts +11 -0
  51. package/dist/lib/Pagination.d.ts.map +1 -0
  52. package/dist/lib/ProgressBar.d.ts +15 -0
  53. package/dist/lib/ProgressBar.d.ts.map +1 -0
  54. package/dist/lib/Section.d.ts +23 -0
  55. package/dist/lib/Section.d.ts.map +1 -0
  56. package/dist/lib/SectionLabel.d.ts +9 -0
  57. package/dist/lib/SectionLabel.d.ts.map +1 -0
  58. package/dist/lib/Select.d.ts +17 -0
  59. package/dist/lib/Select.d.ts.map +1 -0
  60. package/dist/lib/Sidebar.d.ts +20 -0
  61. package/dist/lib/Sidebar.d.ts.map +1 -0
  62. package/dist/lib/Skeleton.d.ts +11 -0
  63. package/dist/lib/Skeleton.d.ts.map +1 -0
  64. package/dist/lib/Spacer.d.ts +8 -0
  65. package/dist/lib/Spacer.d.ts.map +1 -0
  66. package/dist/lib/Spinner.d.ts +13 -0
  67. package/dist/lib/Spinner.d.ts.map +1 -0
  68. package/dist/lib/Stack.d.ts +18 -0
  69. package/dist/lib/Stack.d.ts.map +1 -0
  70. package/dist/lib/StatsCard.d.ts +16 -0
  71. package/dist/lib/StatsCard.d.ts.map +1 -0
  72. package/dist/lib/StructuredData.d.ts +5 -0
  73. package/dist/lib/StructuredData.d.ts.map +1 -0
  74. package/dist/lib/Switch.d.ts +13 -0
  75. package/dist/lib/Switch.d.ts.map +1 -0
  76. package/dist/lib/Table.d.ts +25 -0
  77. package/dist/lib/Table.d.ts.map +1 -0
  78. package/dist/lib/Tabs.d.ts +17 -0
  79. package/dist/lib/Tabs.d.ts.map +1 -0
  80. package/dist/lib/Text.d.ts +12 -0
  81. package/dist/lib/Text.d.ts.map +1 -0
  82. package/dist/lib/Textarea.d.ts +10 -0
  83. package/dist/lib/Textarea.d.ts.map +1 -0
  84. package/dist/lib/ThemeProvider.d.ts +14 -0
  85. package/dist/lib/ThemeProvider.d.ts.map +1 -0
  86. package/dist/lib/ThemeToggle.d.ts +2 -0
  87. package/dist/lib/ThemeToggle.d.ts.map +1 -0
  88. package/dist/lib/Toast.d.ts +18 -0
  89. package/dist/lib/Toast.d.ts.map +1 -0
  90. package/dist/lib/Tooltip.d.ts +13 -0
  91. package/dist/lib/Tooltip.d.ts.map +1 -0
  92. package/dist/lib/index.d.ts +89 -0
  93. package/dist/lib/index.d.ts.map +1 -0
  94. package/dist/lib/styles/formStyles.d.ts +42 -0
  95. package/dist/lib/styles/formStyles.d.ts.map +1 -0
  96. package/dist/lib/styles/semanticVariants.d.ts +22 -0
  97. package/dist/lib/styles/semanticVariants.d.ts.map +1 -0
  98. package/dist/lib/types.d.ts +3 -0
  99. package/dist/lib/types.d.ts.map +1 -0
  100. package/dist/lib/utils/ErrorBoundary.d.ts +21 -0
  101. package/dist/lib/utils/ErrorBoundary.d.ts.map +1 -0
  102. package/dist/lib/utils/ModalErrorBoundary.d.ts +21 -0
  103. package/dist/lib/utils/ModalErrorBoundary.d.ts.map +1 -0
  104. package/dist/lib/utils/cn.d.ts +3 -0
  105. package/dist/lib/utils/cn.d.ts.map +1 -0
  106. package/dist/lib/utils/index.d.ts +5 -0
  107. package/dist/lib/utils/index.d.ts.map +1 -0
  108. package/dist/lib/utils/validateProps.d.ts +6 -0
  109. package/dist/lib/utils/validateProps.d.ts.map +1 -0
  110. package/dist/test-setup.d.ts +1 -0
  111. package/dist/test-setup.d.ts.map +1 -0
  112. package/package.json +79 -0
  113. package/src/styles/indigo-theme.css +322 -0
  114. package/src/styles/preview.scss +4 -0
  115. package/src/styles/pyre-storybook-preview.css +50 -0
  116. package/src/styles/pyre-theme.css +346 -0
package/LICENSE.md ADDED
@@ -0,0 +1,117 @@
1
+ # Functional Source License, Version 1.1, MIT Future License
2
+
3
+ ## Abbreviation
4
+
5
+ FSL-1.1-MIT
6
+
7
+ ## Notice
8
+
9
+ Copyright 2026 Daniel Joffe
10
+
11
+ ## Terms and Conditions
12
+
13
+ ### Licensor ("We")
14
+
15
+ The party offering the Software under these Terms and Conditions.
16
+
17
+ ### The Software
18
+
19
+ The "Software" is each version of the software that we make available
20
+ under these Terms and Conditions, as indicated by our inclusion of these
21
+ Terms and Conditions with the Software.
22
+
23
+ ### License Grant
24
+
25
+ Subject to your compliance with this License Grant and the Patents,
26
+ Redistribution and Trademark clauses below, we hereby grant you the right
27
+ to use, copy, modify, create derivative works, publish, and redistribute
28
+ the Software, in whole or in part, solely for any Permitted Purpose.
29
+
30
+ ### Permitted Purpose
31
+
32
+ A Permitted Purpose is any purpose other than a Competing Use. A
33
+ Competing Use means making the Software available to others in a
34
+ commercial product or service that:
35
+
36
+ 1. substitutes for the Software;
37
+
38
+ 2. substitutes for any other product or service we offer using the
39
+ Software that exists as of the date we make the Software available; or
40
+
41
+ 3. offers the same or substantially similar functionality as the Software.
42
+
43
+ Permitted Purposes specifically include using the Software:
44
+
45
+ 1. for your internal use and access;
46
+
47
+ 2. for non-commercial education;
48
+
49
+ 3. for non-commercial research; and
50
+
51
+ 4. in connection with professional services that you provide to a
52
+ licensee using the Software in accordance with these Terms and
53
+ Conditions.
54
+
55
+ ### Patents
56
+
57
+ To the extent your use for a Permitted Purpose would necessarily infringe
58
+ our patents, the license grant above includes a license under our patents.
59
+ If you make a claim against any party that the Software infringes or
60
+ contributes to the infringement of any patent, then your patent license
61
+ to the Software ends immediately.
62
+
63
+ ### Redistribution
64
+
65
+ The Terms and Conditions apply to all copies, modifications and
66
+ derivatives of the Software.
67
+
68
+ If you redistribute any copies, modifications or derivatives of the
69
+ Software, you must include a copy of or a link to these Terms and
70
+ Conditions and not remove any copyright notices provided in or with the
71
+ Software.
72
+
73
+ ### Disclaimer
74
+
75
+ THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND,
76
+ INCLUDING WITHOUT LIMITATION WARRANTIES OF MERCHANTABILITY, FITNESS FOR
77
+ A PARTICULAR PURPOSE, NON-INFRINGEMENT AND TITLE.
78
+
79
+ OUR TOTAL LIABILITY TO YOU FOR ANY DAMAGES ARISING OUT OF OR RELATED TO
80
+ THIS AGREEMENT IS LIMITED TO US$500.
81
+
82
+ WE WILL NOT BE LIABLE TO YOU FOR ANY LOST PROFITS, REVENUES, OR DATA, OR
83
+ FOR CONSEQUENTIAL, INDIRECT, SPECIAL, INCIDENTAL, OR PUNITIVE DAMAGES.
84
+
85
+ ### Trademarks
86
+
87
+ Except for displaying the License Details and identifying us as the
88
+ origin of the Software, you have no right under these Terms and
89
+ Conditions to use our trademarks, trade names, service marks or product
90
+ names.
91
+
92
+ ## Grant of Future License
93
+
94
+ We hereby irrevocably grant you an additional license to use the Software
95
+ under the MIT license that is effective on the second anniversary of the
96
+ date we make the Software available. On or after that date, you may use
97
+ the Software under the MIT license, in which case the following will
98
+ apply:
99
+
100
+ > Permission is hereby granted, free of charge, to any person obtaining a
101
+ > copy of this software and associated documentation files (the
102
+ > "Software"), to deal in the Software without restriction, including
103
+ > without limitation the rights to use, copy, modify, merge, publish,
104
+ > distribute, sublicense, and/or sell copies of the Software, and to
105
+ > permit persons to whom the Software is furnished to do so, subject to
106
+ > the following conditions:
107
+ >
108
+ > The above copyright notice and this permission notice shall be included
109
+ > in all copies or substantial portions of the Software.
110
+ >
111
+ > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
112
+ > OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
113
+ > MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
114
+ > IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
115
+ > CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
116
+ > TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
117
+ > SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,373 @@
1
+ # @danieljoffe/shared-ui
2
+
3
+ A React component library built with Tailwind CSS 4. Provides 40+ accessible, theme-aware UI primitives designed for the danieljoffe.com ecosystem.
4
+
5
+ **Live component catalog**: [ui.danieljoffe.com](https://ui.danieljoffe.com) (Storybook)
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @danieljoffe/shared-ui
11
+ ```
12
+
13
+ ### Peer dependencies
14
+
15
+ | Package | Version | Required |
16
+ | -------------- | ----------- | --------------------------------- |
17
+ | `react` | `^19.0.0` | Yes |
18
+ | `react-dom` | `^19.0.0` | Yes |
19
+ | `tailwindcss` | `^4.0.0` | Yes |
20
+ | `lucide-react` | `>=0.400.0` | Yes (icons in various components) |
21
+
22
+ ### Bundled dependencies
23
+
24
+ These are included in the package and do not need to be installed separately:
25
+
26
+ - `clsx` — conditional class construction
27
+ - `tailwind-merge` — Tailwind class deduplication
28
+
29
+ ```bash
30
+ npm install react react-dom tailwindcss lucide-react
31
+ ```
32
+
33
+ ## Quick start
34
+
35
+ ```tsx
36
+ import { Button } from '@danieljoffe/shared-ui';
37
+ // Or use deep imports for tree-shaking:
38
+ import { Button } from '@danieljoffe/shared-ui/Button';
39
+
40
+ export default function App() {
41
+ return (
42
+ <Button variant='primary' size='md'>
43
+ Get Started
44
+ </Button>
45
+ );
46
+ }
47
+ ```
48
+
49
+ ## Theme setup
50
+
51
+ Import the theme CSS to get design tokens (colors, spacing, typography, shadows, animations):
52
+
53
+ ```css
54
+ /* In your global CSS or Tailwind entry point */
55
+ @import '@danieljoffe/shared-ui/styles/theme.css';
56
+ ```
57
+
58
+ The theme file is **self-contained** with no external imports. It uses Tailwind CSS 4's `@theme` directive so every token is available as both a CSS custom property and a Tailwind utility class (e.g., `bg-brand-500`, `text-text-secondary`, `shadow-md`).
59
+
60
+ > **oklch color space** -- Brand colors use `oklch()` for perceptually uniform lightness across the scale. Semantic surface/text/status colors use hex or `rgba` for maximum browser compatibility.
61
+
62
+ ### Consumer setup (Tailwind CSS 4)
63
+
64
+ ```css
65
+ /* app/globals.css */
66
+ @import 'tailwindcss';
67
+ @import '@danieljoffe/shared-ui/styles/theme.css';
68
+ ```
69
+
70
+ That single import gives you every token below. No `tailwind.config` changes are needed in Tailwind CSS 4 -- the `@theme` directive registers tokens automatically.
71
+
72
+ ### Integration paths
73
+
74
+ | Approach | When to use | How |
75
+ | ---------------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
76
+ | **Full adoption** | New apps built on shared-ui | Import `theme.css` as shown above. All tokens, base styles, dark mode, and keyframes are active. |
77
+ | **Selective adoption** | Existing apps with their own theme | Copy individual `@theme` token groups into your own CSS. Or import `theme.css` and override specific variables in a subsequent `@theme` block. |
78
+
79
+ To override tokens selectively:
80
+
81
+ ```css
82
+ @import '@danieljoffe/shared-ui/styles/theme.css';
83
+
84
+ @theme {
85
+ --color-brand-500: oklch(0.6 0.2 280); /* shift brand toward purple */
86
+ --font-sans: 'Geist', ui-sans-serif, system-ui, sans-serif;
87
+ }
88
+ ```
89
+
90
+ ### Full token reference
91
+
92
+ #### Radius
93
+
94
+ | Token | Value | Tailwind class |
95
+ | --------------- | ---------- | -------------- |
96
+ | `--radius-sm` | `0.375rem` | `rounded-sm` |
97
+ | `--radius-md` | `0.5rem` | `rounded-md` |
98
+ | `--radius-lg` | `0.75rem` | `rounded-lg` |
99
+ | `--radius-xl` | `1rem` | `rounded-xl` |
100
+ | `--radius-full` | `9999px` | `rounded-full` |
101
+
102
+ #### Typography
103
+
104
+ | Token | Value | Tailwind class |
105
+ | ------------- | ----------------------------------------------- | -------------- |
106
+ | `--font-sans` | `'Inter', ui-sans-serif, system-ui, sans-serif` | `font-sans` |
107
+ | `--font-mono` | `'JetBrains Mono', ui-monospace, monospace` | `font-mono` |
108
+
109
+ #### Shadows
110
+
111
+ | Token | Light mode | Dark mode | Tailwind class |
112
+ | ------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------- | -------------- |
113
+ | `--shadow-xs` | `0 1px 2px rgba(0,0,0,0.04)` | `0 1px 2px rgba(0,0,0,0.2)` | `shadow-xs` |
114
+ | `--shadow-sm` | `0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04)` | `0 1px 3px rgba(0,0,0,0.3), 0 1px 2px rgba(0,0,0,0.2)` | `shadow-sm` |
115
+ | `--shadow-md` | `0 4px 6px -1px rgba(0,0,0,0.07), 0 2px 4px -2px rgba(0,0,0,0.05)` | `0 4px 6px -1px rgba(0,0,0,0.3), 0 2px 4px -2px rgba(0,0,0,0.2)` | `shadow-md` |
116
+ | `--shadow-lg` | `0 10px 15px -3px rgba(0,0,0,0.08), 0 4px 6px -4px rgba(0,0,0,0.04)` | `0 10px 15px -3px rgba(0,0,0,0.4), 0 4px 6px -4px rgba(0,0,0,0.2)` | `shadow-lg` |
117
+ | `--shadow-xl` | `0 20px 25px -5px rgba(0,0,0,0.08), 0 8px 10px -6px rgba(0,0,0,0.04)` | `0 20px 25px -5px rgba(0,0,0,0.4), 0 8px 10px -6px rgba(0,0,0,0.2)` | `shadow-xl` |
118
+
119
+ #### Animations
120
+
121
+ | Token | Value | Tailwind class |
122
+ | -------------------------- | --------------------------------------- | ------------------------ |
123
+ | `--animate-fade-in` | `fade-in 0.2s ease-out` | `animate-fade-in` |
124
+ | `--animate-slide-up` | `slide-up 0.2s ease-out` | `animate-slide-up` |
125
+ | `--animate-slide-down` | `slide-down 0.15s ease-out` | `animate-slide-down` |
126
+ | `--animate-slide-out-down` | `slide-out-down 0.15s ease-in forwards` | `animate-slide-out-down` |
127
+ | `--animate-scale-in` | `scale-in 0.15s ease-out` | `animate-scale-in` |
128
+
129
+ Additional keyframes defined (usable in custom animations): `bounceSubtle`, `pulseSlow`.
130
+
131
+ #### Colors -- Brand (blue-indigo, oklch hue 250)
132
+
133
+ | Token | Value | Tailwind class |
134
+ | ------------------- | ---------------------- | ------------------------------------ |
135
+ | `--color-brand-50` | `oklch(0.97 0.01 250)` | `bg-brand-50`, `text-brand-50`, etc. |
136
+ | `--color-brand-100` | `oklch(0.93 0.03 250)` | `bg-brand-100` |
137
+ | `--color-brand-200` | `oklch(0.87 0.06 250)` | `bg-brand-200` |
138
+ | `--color-brand-300` | `oklch(0.78 0.1 250)` | `bg-brand-300` |
139
+ | `--color-brand-400` | `oklch(0.68 0.15 250)` | `bg-brand-400` |
140
+ | `--color-brand-500` | `oklch(0.54 0.19 250)` | `bg-brand-500` |
141
+ | `--color-brand-600` | `oklch(0.5 0.19 250)` | `bg-brand-600` |
142
+ | `--color-brand-700` | `oklch(0.43 0.17 250)` | `bg-brand-700` |
143
+ | `--color-brand-800` | `oklch(0.37 0.14 250)` | `bg-brand-800` |
144
+ | `--color-brand-900` | `oklch(0.3 0.1 250)` | `bg-brand-900` |
145
+ | `--color-brand-950` | `oklch(0.22 0.08 250)` | `bg-brand-950` |
146
+
147
+ #### Colors -- Semantic surface
148
+
149
+ | Token | Light | Dark | Tailwind class |
150
+ | --------------------------- | ----------------- | ----------------- | ---------------------- |
151
+ | `--color-surface` | `#ffffff` | `#0f1117` | `bg-surface` |
152
+ | `--color-surface-secondary` | `#f9fafb` | `#161922` | `bg-surface-secondary` |
153
+ | `--color-surface-tertiary` | `#f3f4f6` | `#1e2130` | `bg-surface-tertiary` |
154
+ | `--color-surface-elevated` | `#ffffff` | `#1a1d2b` | `bg-surface-elevated` |
155
+ | `--color-surface-overlay` | `rgba(0,0,0,0.5)` | `rgba(0,0,0,0.7)` | `bg-surface-overlay` |
156
+
157
+ #### Colors -- Semantic border
158
+
159
+ | Token | Light | Dark | Tailwind class |
160
+ | -------------------------- | ---------------------- | --------- | ------------------------- |
161
+ | `--color-border` | `#e5e7eb` | `#2a2d3a` | `border-border` |
162
+ | `--color-border-secondary` | `#d1d5db` | `#3a3d4a` | `border-border-secondary` |
163
+ | `--color-border-focus` | `oklch(0.54 0.19 250)` | _(same)_ | `outline-border-focus` |
164
+
165
+ #### Colors -- Semantic text
166
+
167
+ | Token | Light | Dark | Tailwind class |
168
+ | ------------------------ | --------------------- | --------- | --------------------- |
169
+ | `--color-text-primary` | `#111827` | `#f1f5f9` | `text-text-primary` |
170
+ | `--color-text-secondary` | `#6b7280` | `#94a3b8` | `text-text-secondary` |
171
+ | `--color-text-tertiary` | `#9ca3af` | `#64748b` | `text-text-tertiary` |
172
+ | `--color-text-inverse` | `#ffffff` | `#0f1117` | `text-text-inverse` |
173
+ | `--color-text-brand` | `oklch(0.5 0.19 250)` | _(same)_ | `text-text-brand` |
174
+
175
+ #### Colors -- Status
176
+
177
+ | Token | Light | Dark | Tailwind class |
178
+ | ----------------------- | --------- | --------- | ---------------------------- |
179
+ | `--color-success` | `#10b981` | _(same)_ | `text-success`, `bg-success` |
180
+ | `--color-success-light` | `#ecfdf5` | `#052e16` | `bg-success-light` |
181
+ | `--color-warning` | `#f59e0b` | _(same)_ | `text-warning`, `bg-warning` |
182
+ | `--color-warning-light` | `#fffbeb` | `#451a03` | `bg-warning-light` |
183
+ | `--color-error` | `#ef4444` | _(same)_ | `text-error`, `bg-error` |
184
+ | `--color-error-light` | `#fef2f2` | `#450a0a` | `bg-error-light` |
185
+ | `--color-info` | `#2563eb` | _(same)_ | `text-info`, `bg-info` |
186
+ | `--color-info-light` | `#eff6ff` | `#172554` | `bg-info-light` |
187
+
188
+ ### Dark mode
189
+
190
+ Dark mode is handled via `.dark` class overrides in the same theme file. The `ThemeProvider` component toggles the `.dark` class on `<html>`. The theme file also registers a custom variant:
191
+
192
+ ```css
193
+ @custom-variant dark (&:is(.dark *));
194
+ ```
195
+
196
+ This means you can use `dark:` prefix in Tailwind classes (e.g., `dark:bg-surface-secondary`) and they will activate when `.dark` is present on an ancestor element.
197
+
198
+ ### Base styles
199
+
200
+ The theme file includes an `@layer base` block that sets:
201
+
202
+ - Default border and outline colors on all elements
203
+ - Body background, text color, font-family, and line-height
204
+ - Heading typography (h1-h6) with balanced text wrapping and tight letter-spacing
205
+ - Code element styling using `font-mono` with brand-colored background
206
+ - Table and list resets
207
+ - Scrollbar styling (WebKit)
208
+ - `prefers-reduced-motion` support (disables animations automatically)
209
+
210
+ ### Experimental color schemes
211
+
212
+ The `styles/` directory also contains three SCSS-based color scheme variants. These are **experimental** and use a different variable convention from the main `theme.css` (e.g., `--accent`, `--background` instead of `--color-brand-*`, `--color-surface`). They are not currently exported in `package.json` and are not intended for consumer use:
213
+
214
+ | Scheme | Directory | Accent color |
215
+ | ------------ | ---------------------- | ------------ |
216
+ | Deep Teal | `styles/deep-teal/` | `#0d7377` |
217
+ | Muted Violet | `styles/muted-violet/` | `#6b5b95` |
218
+ | Warm Gold | `styles/warm-gold/` | `#9a6c18` |
219
+
220
+ Each includes a `default.scss` (light) and `dark.scss` file.
221
+
222
+ ## Component catalog
223
+
224
+ ### Typography
225
+
226
+ | Component | Description | Key props |
227
+ | ----------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
228
+ | **Heading** | Semantic headings with predefined variants | `variant`: `hero`, `detail`, `subtitle`, `section`, `cardTitle`, `component`, `mdxH1`-`mdxH4`. `as`: override element (`h1`-`h6`, `p`) |
229
+ | **Text** | Body text with semantic variants | `variant`: `body`, `bodyLg`, `subtitle`, `cardDescription`, `detail`, `label`, `meta`, `caption`, `helper`, `error`. `as`: override element (`p`, `span`, `div`, `label`) |
230
+
231
+ ### Layout
232
+
233
+ | Component | Description | Key props |
234
+ | ----------------------- | ------------------------------------------ | -------------------------------------- |
235
+ | **Container** | Max-width centered container | `size`: `sm`, `md`, `lg`, `xl`, `full` |
236
+ | **Grid** / **GridItem** | CSS Grid wrapper | `cols`, `gap`, `responsive` |
237
+ | **PageContainer** | Page-level wrapper with consistent padding | `wide` |
238
+ | **PageLayout** | Full page layout with sections | `wide` |
239
+ | **Section** | Semantic section with spacing | `id`, `className` |
240
+ | **SectionLabel** | Section header with icon + label | `icon`, `label` |
241
+ | **Spacer** | Vertical/horizontal spacing | `size`: `xs`-`xl` |
242
+ | **Stack** | Flex stack (vertical/horizontal) | `direction`, `gap`, `align`, `justify` |
243
+ | **Sidebar** | Navigation sidebar | `items`, `activeItem` |
244
+
245
+ ### Data display
246
+
247
+ | Component | Description | Key props |
248
+ | ---------------------------------------------------------------------------- | ------------------------------------------- | ----------------------------------------------------------------------------------- |
249
+ | **Avatar** | User avatar with fallback | `src`, `alt`, `size` |
250
+ | **Badge** | Status/category label | `variant`: `default`, `brand`, `brand-solid`, `success`, `warning`, `error`, `info` |
251
+ | **Breadcrumb** | Navigation breadcrumb trail | `items: BreadcrumbItem[]` |
252
+ | **Card** / **CardHeader** / **CardTitle** / **CardContent** / **CardFooter** | Composable card | Standard card sections |
253
+ | **CTACard** | Call-to-action card with heading and action | `title`, `description`, `action` |
254
+ | **Divider** | Horizontal rule with optional label | `label` |
255
+ | **Kbd** | Keyboard shortcut display | `children` (key text) |
256
+ | **Pagination** | Page navigation controls | `currentPage`, `totalPages`, `onPageChange` |
257
+ | **ProgressBar** | Progress indicator | `value`, `max`, `variant` |
258
+ | **Skeleton** | Loading placeholder | `width`, `height`, `variant`: `text`, `circular`, `rectangular` |
259
+ | **StatsCard** | Metric display card | `label`, `value`, `subtitle`, `trend` |
260
+ | **StructuredData** | JSON-LD script injection | `data` |
261
+ | **Table** | Data table with sorting | `columns: Column[]`, `data`, `sortable` |
262
+ | **Tabs** | Tab navigation | `tabs: Tab[]`, `activeTab`, `onTabChange` |
263
+ | **Tooltip** | Hover tooltip | `content`, `position` |
264
+
265
+ ### Forms
266
+
267
+ | Component | Description | Key props |
268
+ | ------------------ | ------------------------------ | ----------------------------------------------------------------------------------------- |
269
+ | **Button** | Button with variants and sizes | `variant`: `primary`, `secondary`, `ghost`, `outline`, `danger`. `size`: `sm`, `md`, `lg` |
270
+ | **Checkbox** | Checkbox input | `checked`, `onChange`, `label` |
271
+ | **FormFieldError** | Validation error message | `message`, `id` (for `aria-describedby`) |
272
+ | **Input** | Text input with label/error | `label`, `error`, `helper` |
273
+ | **Select** | Select dropdown | `options: SelectOption[]`, `label`, `error` |
274
+ | **Switch** | Toggle switch | `checked`, `onChange`, `label` |
275
+ | **Textarea** | Multi-line text input | `label`, `error`, `helper` |
276
+
277
+ ### Feedback
278
+
279
+ | Component | Description | Key props |
280
+ | -------------------------------- | ------------------------- | --------------------------------------------------------------------- |
281
+ | **Alert** | Contextual alert banner | `variant`: `info`, `success`, `warning`, `error`. `title`, `children` |
282
+ | **Dropdown** | Action menu | `items: DropdownItem[]`, `trigger` |
283
+ | **Loading** | Full-area loading spinner | `message` |
284
+ | **Modal** | Dialog overlay | `open`, `onClose`, `title` |
285
+ | **Spinner** | Inline loading indicator | `size`: `sm`, `md`, `lg` |
286
+ | **ToastProvider** / **useToast** | Toast notification system | `toast({ variant, title, description })` |
287
+
288
+ ### Theming
289
+
290
+ | Component | Description | Key props |
291
+ | -------------------------------- | ----------------------------- | ------------------------------------------ |
292
+ | **ThemeProvider** / **useTheme** | Theme context provider | Wraps app, provides `theme`, `toggleTheme` |
293
+ | **ThemeToggle** | Dark/light mode toggle button | Uses `useTheme` internally |
294
+
295
+ ### Decorative
296
+
297
+ | Component | Description | Key props |
298
+ | --------------- | ---------------------------- | ----------- |
299
+ | **AspectRatio** | Fixed aspect ratio container | `ratio` |
300
+ | **GridBg** | Decorative grid background | `className` |
301
+
302
+ ## Utilities
303
+
304
+ | Export | Description |
305
+ | ----------------------------- | -------------------------------------------------------- |
306
+ | `cn(...classes)` | Tailwind-aware class merging (`clsx` + `tailwind-merge`) |
307
+ | `validateProps(props, rules)` | Runtime prop validation helper |
308
+ | `ErrorBoundary` | React error boundary component |
309
+ | `ModalErrorBoundary` | Error boundary scoped to modals |
310
+
311
+ ## TypeScript
312
+
313
+ All components export their prop types (e.g., `ButtonProps`, `AlertProps`). The library is built with `exactOptionalPropertyTypes` compatibility.
314
+
315
+ ```tsx
316
+ import type { ButtonProps, ButtonVariant } from '@danieljoffe/shared-ui';
317
+ ```
318
+
319
+ ## Tree-shaking
320
+
321
+ Use deep imports for optimal bundle size:
322
+
323
+ ```tsx
324
+ // Only bundles Button code:
325
+ import { Button } from '@danieljoffe/shared-ui/Button';
326
+
327
+ // Bundles everything (barrel import):
328
+ import { Button } from '@danieljoffe/shared-ui';
329
+ ```
330
+
331
+ ## React 19 ref pattern
332
+
333
+ Components accept `ref` as a regular prop — `forwardRef` is not used. This is the React 19 pattern:
334
+
335
+ ```tsx
336
+ import { useRef } from 'react';
337
+ import { Input } from '@danieljoffe/shared-ui/Input';
338
+
339
+ function MyForm() {
340
+ const inputRef = useRef<HTMLInputElement>(null);
341
+ return <Input ref={inputRef} label='Name' />;
342
+ }
343
+ ```
344
+
345
+ ## Constraints
346
+
347
+ - **No Next.js APIs**: This library depends only on React and Tailwind CSS. Components requiring `next/link`, `next/image`, `useRouter`, etc. belong in the consuming app's `components/kit/` directory.
348
+ - **No framework-specific code**: Components work in any React 19+ environment with Tailwind CSS 4.
349
+
350
+ ## Development
351
+
352
+ ```bash
353
+ # Run unit tests
354
+ pnpm nx test @danieljoffe/shared-ui
355
+
356
+ # Start Storybook
357
+ pnpm nx storybook @danieljoffe/shared-ui
358
+
359
+ # Build library
360
+ pnpm nx build @danieljoffe/shared-ui
361
+ ```
362
+
363
+ ## Contributing
364
+
365
+ This library lives in the [danieljoffe.com monorepo](https://github.com/danieljoffe/danieljoffe.com) at `libs/shared/ui/`. See the root `CLAUDE.md` for conventions around the Rule of Three, component patterns, and the shared-ui boundary.
366
+
367
+ ## License
368
+
369
+ [FSL-1.1-MIT](./LICENSE.md) — Functional Source License 1.1 with MIT Future License.
370
+
371
+ The Functional Source License grants permission to use, copy, modify, and redistribute the software for any purpose except a Competing Use (a commercial product or service that substitutes for this library or anything we offer using it). Two years after each release, that release automatically re-licenses under the MIT License. Read the [LICENSE](./LICENSE.md) for the full terms.
372
+
373
+ Practical translation: you can use, fork, and self-host this library freely for personal, internal, educational, or non-competing-commercial work. If you'd like to use it inside a competing commercial offering, [open an issue](https://github.com/danieljoffe/danieljoffe.com/issues) — there's almost certainly a path that works for both of us.
@@ -0,0 +1,2 @@
1
+ export * from './lib';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,OAAO,CAAC"}