@darthcharles/greda 0.1.0 → 1.0.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
@@ -1,263 +1,160 @@
1
- # @darthcharles/greda
1
+ # Greda
2
2
 
3
- A reusable React/TypeScript package built from **shadcn/ui component source**,
4
- with a complete neutral base theme and consumer-owned token overrides.
5
- Storybook documents each component in its own `*.stories.tsx` file.
3
+ Composable React components with a neutral base theme and CSS tokens you control.
4
+ Built from shadcn/ui New York and Radix primitives, with compiled styles and
5
+ TypeScript declarations. Supports React 18 and 19; no Tailwind setup required.
6
+
7
+ **[Explore Storybook](https://elegant-hotteok-cbb834.netlify.app/)** ·
8
+ [Getting started](https://elegant-hotteok-cbb834.netlify.app/?path=/docs/introduction-overview--getting-started) ·
9
+ [Design tokens](https://elegant-hotteok-cbb834.netlify.app/?path=/story/design-tokens--overview)
6
10
 
7
11
  ## Quick start
8
12
 
13
+ Install into an existing React application with React and React DOM 18.2+ or 19:
14
+
15
+ ```bash
16
+ npm install @darthcharles/greda
17
+ ```
18
+
19
+ Import the stylesheet once at your application's entry point:
20
+
9
21
  ```tsx
10
- import { Button, Input, Label } from "@darthcharles/greda";
11
22
  import "@darthcharles/greda/styles.css";
12
- import "./theme.css";
23
+ ```
24
+
25
+ Then import the components you need:
26
+
27
+ ```tsx
28
+ import { Button } from "@darthcharles/greda";
13
29
 
14
30
  export function Example() {
15
- return (
16
- <>
17
- <Label htmlFor="name">Name</Label>
18
- <Input id="name" />
19
- <Button>Continue</Button>
20
- </>
21
- );
31
+ return <Button>Continue</Button>;
22
32
  }
23
33
  ```
24
34
 
25
- React 18 and 19 are peer dependencies and are excluded from the ESM build.
26
- Import the stylesheet once at the application entry point. CSS is compiled by
27
- the package: consumers need **no Tailwind setup**. Utility classes use the `ds-`
28
- prefix, and the baseline reset and Tailwind variable initialization are scoped to `.comal-ui`. Page layout, page
29
- backgrounds, and fonts remain the application's responsibility.
35
+ The stylesheet includes a ready-to-use base theme, CSS tokens, and scoped
36
+ component styles. No custom theme or additional CSS is required to use Greda.
37
+ Your application controls its own page layout, backgrounds, and fonts. React
38
+ and React DOM remain peer dependencies outside the ESM bundle.
30
39
 
31
- ```css
32
- body {
33
- background: hsl(var(--background));
34
- color: hsl(var(--foreground));
35
- font-family: var(--font-sans);
36
- }
37
- ```
40
+ Visit [Storybook](https://elegant-hotteok-cbb834.netlify.app/) for component APIs,
41
+ variants, interaction examples, and accessibility guidance.
38
42
 
39
- ## Consumer themes
43
+ Greda and application markup share unprefixed utility classes such as
44
+ `surface-primary`, `text-primary`, and `border-primary`. Pass them through
45
+ `className` to override component defaults. See the introduction guide for supported
46
+ utilities and migration from `ds-*` classes.
40
47
 
41
- Override any subset of tokens. Unspecified tokens retain their base values.
42
- Colors use **space-separated HSL channels**, not hex or complete `hsl()` calls.
43
- The overlay token additionally includes alpha. Other tokens accept CSS values.
44
- Token names and value formats are part of the versioned public API.
48
+ ## With Tailwind 4
49
+
50
+ Import Greda's tokens in your app's Tailwind CSS entry point. This registers the
51
+ theme so Tailwind can generate utilities without a preset or manual mappings:
45
52
 
46
53
  ```css
47
- /* The consumer's theme.css */
48
- :root {
49
- --primary: 9.500 55.556% 42.353%;
50
- --primary-hover: 9.714 56.150% 36.667%;
51
- --primary-foreground: 0 0% 100%;
52
- --ring: 9.500 55.556% 42.353%;
53
- --radius: 0.75rem;
54
- }
54
+ @import "tailwindcss";
55
+ @import "@darthcharles/greda/tokens.css";
56
+ ```
57
+
58
+ ```tsx
59
+ <div className="bg-primary p-4 text-primary-foreground hover:bg-primary-hover">
60
+ Your content
61
+ </div>
55
62
  ```
56
63
 
57
- Base token selectors use `:where()` so ordinary consumer selectors win even
58
- if the library CSS loads later. Keep background/foreground pairs readable and
59
- verify contrast in your theme. The base theme downloads no external fonts.
60
- Comal's brand overrides belong in its consuming application.
61
-
62
- Set `data-color-scheme="dark"` on `<html>` to opt into the base dark palette.
63
- Brand identity and color scheme are independent. A consumer can customize dark
64
- mode using `:root[data-color-scheme="dark"]` overrides.
65
-
66
- Apply themes on `<html>` so portaled dialogs, selects, and tooltips inherit them.
67
- For themes scoped to a subtree, pass the themed element to the content component's
68
- `container` prop. `@darthcharles/greda/tokens.css` exports just the tokens;
69
- `styles.css` already includes them.
70
-
71
- ### Base token reference
72
-
73
- | Token | Default |
74
- | --- | --- |
75
- | `--background` | `0 0% 100%` |
76
- | `--foreground` | `222 47% 11%` |
77
- | `--card` | `0 0% 100%` |
78
- | `--card-foreground` | `222 47% 11%` |
79
- | `--popover` | `0 0% 100%` |
80
- | `--popover-foreground` | `222 47% 11%` |
81
- | `--accent` | `220 20% 94%` |
82
- | `--accent-foreground` | `222 35% 20%` |
83
- | `--primary` | `220 80% 45%` |
84
- | `--primary-foreground` | `0 0% 100%` |
85
- | `--primary-hover` | `220 80% 38%` |
86
- | `--secondary` | `220 20% 94%` |
87
- | `--secondary-foreground` | `222 35% 20%` |
88
- | `--muted` | `220 15% 96%` |
89
- | `--muted-foreground` | `220 10% 42%` |
90
- | `--border` | `220 15% 82%` |
91
- | `--input` | `220 15% 82%` |
92
- | `--ring` | `220 80% 45%` |
93
- | `--destructive` | `0 72% 43%` |
94
- | `--destructive-foreground` | `0 0% 100%` |
95
- | `--overlay` | `222 47% 11% / 0.4` |
96
- | `--font-sans` | `system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif` |
97
- | `--font-heading` | `var(--font-sans)` |
98
- | `--font-size-xs` | `0.75rem` |
99
- | `--font-size-sm` | `0.8125rem` |
100
- | `--font-size-md` | `0.875rem` |
101
- | `--font-size-lg` | `1.25rem` |
102
- | `--font-weight-medium` | `500` |
103
- | `--font-weight-bold` | `600` |
104
- | `--line-height` | `1.5` |
105
- | `--spacing-unit` | `0.25rem` |
106
- | `--space-0` | `calc(var(--spacing-unit) * 0)` |
107
- | `--space-0-5` | `calc(var(--spacing-unit) * 0.5)` |
108
- | `--space-1` | `calc(var(--spacing-unit) * 1)` |
109
- | `--space-1-5` | `calc(var(--spacing-unit) * 1.5)` |
110
- | `--space-2` | `calc(var(--spacing-unit) * 2)` |
111
- | `--space-2-5` | `calc(var(--spacing-unit) * 2.5)` |
112
- | `--space-3` | `calc(var(--spacing-unit) * 3)` |
113
- | `--space-3-5` | `calc(var(--spacing-unit) * 3.5)` |
114
- | `--space-4` | `calc(var(--spacing-unit) * 4)` |
115
- | `--space-5` | `calc(var(--spacing-unit) * 5)` |
116
- | `--space-6` | `calc(var(--spacing-unit) * 6)` |
117
- | `--space-8` | `calc(var(--spacing-unit) * 8)` |
118
- | `--space-9` | `calc(var(--spacing-unit) * 9)` |
119
- | `--space-10` | `calc(var(--spacing-unit) * 10)` |
120
- | `--space-12` | `calc(var(--spacing-unit) * 12)` |
121
- | `--space-16` | `calc(var(--spacing-unit) * 16)` |
122
- | `--space-20` | `calc(var(--spacing-unit) * 20)` |
123
- | `--space-24` | `calc(var(--spacing-unit) * 24)` |
124
- | `--space-32` | `calc(var(--spacing-unit) * 32)` |
125
- | `--radius` | `0.5rem` |
126
- | `--radius-sm` | `calc(var(--radius) * 0.6)` |
127
- | `--radius-full` | `9999px` |
128
- | `--radius-lg` | `calc(var(--radius) * 1.5)` |
129
- | `--border-width` | `1px` |
130
- | `--dialog-width` | `32.5rem` |
131
- | `--tooltip-width` | `20rem` |
132
- | `--toast-width` | `24rem` |
133
- | `--shadow-sm` | `0 1px 2px hsl(222 47% 11% / 0.08)` |
134
- | `--shadow` | `0 8px 24px hsl(222 47% 11% / 0.12)` |
135
- | `--shadow-dialog` | `0 24px 70px hsl(222 47% 11% / 0.24)` |
136
- | `--focus-width` | `2px` |
137
- | `--focus-offset` | `2px` |
138
- | `--disabled-opacity` | `0.5` |
139
- | `--duration` | `150ms` |
140
- | `--z-overlay` | `50` |
141
- | `--z-popover` | `60` |
142
- | `--z-toast` | `70` |
143
-
144
- The **Design Tokens** section in Storybook shows every token's value and a live
145
- preview: color swatches, typography samples, spacing bars, corner shapes, shadows,
146
- focus outlines, motion, opacity, sizing, and stacking layers. Its examples update
147
- with the theme and color-scheme toolbar. The catalog reads directly from the token
148
- stylesheet. Run `npm run docs:tokens` after editing tokens to refresh the reference
149
- table in this README.
150
-
151
- `--spacing-unit` scales the spacing utilities used by components. Named steps such
152
- as `--space-1` (4px), `--space-2` (8px), `--space-4` (16px), and `--space-8` (32px)
153
- derive from that unit. Override a single step to adjust every component utility
154
- that uses it. Fractional steps use a hyphen, for example `--space-0-5` (2px).
64
+ To use Greda components too, add
65
+ `@import "@darthcharles/greda/styles.css" layer(components);` between those two
66
+ imports, instead of importing `styles.css` again in JavaScript. The layer lets
67
+ your generated utilities override component styles, including responsive variants.
68
+ The import-only theme integration requires Tailwind 4; Greda's precompiled
69
+ component styles also work without Tailwind.
70
+
71
+ ## Theming
72
+
73
+ Customization is optional. To change the default theme, import your own
74
+ `theme.css` after the package stylesheet and override the base colors or other
75
+ tokens you need.
76
+ Unspecified values keep their base defaults, and components and color utilities
77
+ automatically use your overrides:
155
78
 
156
79
  ```css
80
+ /* theme.css */
157
81
  :root {
158
- --space-4: 1.25rem; /* Change the step used by px-4 and p-4. */
82
+ --base-primary: 9.500 55.556% 42.353%;
83
+ --base-primary-hover: 9.714 56.150% 36.667%;
84
+ --radius: 0.75rem;
159
85
  }
160
86
  ```
161
87
 
162
- Radii, fonts,
163
- shadows, focus, and semantic colors are also mapped through Tailwind to runtime
164
- variables. Breakpoints are build-time application layout choices rather than
165
- CSS variable tokens. Components fit their containers; dialog and toast content
166
- are constrained to the viewport. Use composition and `className` for layout
167
- changes and application-specific styling.
168
-
169
- ## Components and Storybook
170
-
171
- | Component | Storybook source |
172
- | --------- | -------------------------------------------------------------------- |
173
- | Button | [src/stories/Button.stories.tsx](src/stories/Button.stories.tsx) |
174
- | Input | [src/stories/Input.stories.tsx](src/stories/Input.stories.tsx) |
175
- | Textarea | [src/stories/Textarea.stories.tsx](src/stories/Textarea.stories.tsx) |
176
- | Label | [src/stories/Label.stories.tsx](src/stories/Label.stories.tsx) |
177
- | Select | [src/stories/Select.stories.tsx](src/stories/Select.stories.tsx) |
178
- | Dialog | [src/stories/Dialog.stories.tsx](src/stories/Dialog.stories.tsx) |
179
- | Card | [src/stories/Card.stories.tsx](src/stories/Card.stories.tsx) |
180
- | Badge | [src/stories/Badge.stories.tsx](src/stories/Badge.stories.tsx) |
181
- | Checkbox | [src/stories/Checkbox.stories.tsx](src/stories/Checkbox.stories.tsx) |
182
- | Tabs | [src/stories/Tabs.stories.tsx](src/stories/Tabs.stories.tsx) |
183
- | Toast | [src/stories/Toast.stories.tsx](src/stories/Toast.stories.tsx) |
184
- | Tooltip | [src/stories/Tooltip.stories.tsx](src/stories/Tooltip.stories.tsx) |
185
-
186
- Each file contains default usage and relevant variants, sizes, disabled/loading,
187
- validation, and keyboard documentation. The toolbar previews base/Comal themes
188
- and light/dark color schemes. Noninteractive components do not invent loading
189
- or disabled behavior. `npm run validate:stories` enforces one story file per
190
- component. Storybook includes automatic prop documentation and an axe panel.
191
-
192
- The public API follows shadcn's composition model:
193
-
194
- - Button: `default`, `secondary`, `outline`, `ghost`, `destructive`, `link` variants;
195
- `default`, `sm`, `lg`, `icon` sizes. `loading` sets `aria-busy` and disables native
196
- buttons. Set a useful label such as `Saving…`. The default type is `button`;
197
- pass `type="submit"` for forms. `asChild` supports links; disabled/loading
198
- behavior is intended for native buttons.
199
- - Input, Textarea, Label: native field props and refs. Associate visible labels
200
- and connect errors using `aria-invalid` and `aria-describedby`.
201
- - Select: SelectTrigger, SelectValue, SelectContent, SelectGroup, SelectLabel,
202
- SelectItem, SelectSeparator, SelectScrollUpButton, SelectScrollDownButton.
203
- Controlled values use `onValueChange`.
204
- - Dialog: DialogTrigger, DialogContent, DialogTitle, DialogDescription,
205
- DialogHeader, DialogFooter, DialogClose, DialogOverlay, DialogPortal. Content
206
- includes a built-in accessible close button. Include a title. Explicitly pass
207
- `aria-describedby={undefined}` when there is no description.
208
- - Card: CardHeader, CardTitle, CardDescription, CardContent, CardFooter.
209
- - Badge: `default`, `secondary`, `outline`, `destructive` variants.
210
- - Checkbox: boolean or `indeterminate` state; controlled `onCheckedChange`.
211
- - Tabs: TabsList, TabsTrigger, TabsContent; horizontal/vertical orientation.
212
- - Toast: ToastProvider, ToastViewport, ToastTitle, ToastDescription, ToastAction,
213
- ToastClose. Supply one provider/viewport and control each toast's open state.
214
- - Tooltip: TooltipProvider, TooltipTrigger, TooltipContent.
215
-
216
- Radix remains a dependency of the shadcn components and supplies their keyboard
217
- and ARIA behavior. Give icon buttons accessible names and keep critical errors
218
- inline. `cn` is exported from the root and `@darthcharles/greda/utils`; it combines
219
- conditional classes and merges conflicting `ds-` Tailwind utilities.
88
+ Colors use space-separated HSL channels without an `hsl()` wrapper;
89
+ `--overlay` also includes alpha. Other tokens accept ordinary CSS values.
90
+
91
+ Consumers override only the generic base colors: `--base-primary`,
92
+ `--base-primary-hover`, `--base-secondary`, `--base-tertiary`, `--base-accent`,
93
+ `--base-destructive`, `--base-white`, `--base-black`, and selected
94
+ `--base-gray-*` steps (50, 100, 200, 300, 500, 600, 800, 900). Every theme uses
95
+ the same names. Greda assigns them to independent roles and utilities:
96
+
97
+ | Category | Utility examples | Default light base colors |
98
+ | --- | --- | --- |
99
+ | Surfaces | `surface-primary`, `surface-secondary`, `surface-tertiary`, `surface-inverse` | White, gray-100, tertiary, black |
100
+ | Text | `text-primary`, `text-secondary`, `text-muted`, `text-inverse` | Black, gray-600, gray-500, white |
101
+ | Borders | `border-primary`, `border-secondary`, `border-inverse` | Gray-200, gray-100, gray-600 |
102
+
103
+ `bg-primary` / `bg-secondary` use base primary/secondary action colors.
104
+ Primary text and borders use their neutral base assignments independently.
105
+ Dark assignments are built in. Define base overrides on `:root` or a
106
+ `data-theme` element; scoped themes automatically rebind the role variables.
107
+ Consumers do not need to declare surface, text, or border tokens.
108
+ `surface-tertiary` uses `--base-tertiary`: soft blue (`#eff6ff`) in Base and
109
+ light green (`#e8eee0`) in Comal, with darker counterparts in dark mode.
110
+ Override `--base-tertiary` to customize this surface independently of accent
111
+ backgrounds and hover states.
112
+
113
+ `styles.css` includes role utilities and hover/focus variants; the Tailwind 4
114
+ import also generates responsive and opacity variants. Start with Storybook's
115
+ **Design Tokens → Overview**. **Base colors** shows the palette you customize,
116
+ with brand/state colors first and neutrals second. **Color roles** combines
117
+ surface, text, and border assignments with live examples. Other foundations
118
+ have separate pages.
119
+
120
+ **Migration:** move color overrides to the generic base slots above. In particular,
121
+ `--primary` becomes `--base-primary`. `text-primary`, `text-secondary`,
122
+ `text-muted`, `border-primary`, and `border-secondary` use category roles
123
+ rather than the matching action color. Use `text-action` / `border-action`
124
+ for action colors. Existing component tokens remain as derived variables;
125
+ Greda owns their assignments. Tooltip uses inverse surface/text roles.
126
+
127
+ Spacing tokens such as `--space-4` are shared distances for padding, margin, and
128
+ gap. Use them directly in your own CSS when needed; layout rules are optional
129
+ examples, not theme setup. If you only need tokens, import
130
+ `@darthcharles/greda/tokens.css` instead of `styles.css`.
131
+
132
+ Set `data-color-scheme="dark"` on `<html>` for the base dark palette. Brand
133
+ overrides need matching dark colors. Apply themes to `<html>` so portaled
134
+ content inherits them; scoped themes use the content component's `container` prop.
135
+
136
+ The [introduction guide](https://elegant-hotteok-cbb834.netlify.app/?path=/docs/introduction-overview--getting-started)
137
+ covers fonts, dark mode, and scoped portals. Browse the
138
+ [interactive token catalog](https://elegant-hotteok-cbb834.netlify.app/?path=/story/design-tokens--overview)
139
+ or read the defaults in [tokens.css](src/styles/tokens.css).
220
140
 
221
141
  ## Development
222
142
 
223
- Use Node 24 (`nvm use`) and npm:
143
+ Use Node 24 and the committed npm lockfile:
224
144
 
225
145
  ```bash
146
+ nvm use
226
147
  npm ci
227
- npm run storybook # http://localhost:6006
228
- npm run check # types, interactions, stories, package, static catalog
229
- npm run test:browser # render all stories, axe, theme/portal checks
230
- npm pack # build a local package archive
148
+ npm run storybook
231
149
  ```
232
150
 
233
- Local browser tests use installed Google Chrome. CI installs Playwright Chromium.
234
- Build Storybook before running browser tests (`npm run build-storybook`).
235
- Build outputs are ignored: ESM and declarations go to `dist/`, compiled CSS to
236
- `dist/styles.css`, and the static catalog to `storybook-static/`.
237
-
238
- The Comal example in `.storybook/themes/comal.css` uses Meal Planner’s cream,
239
- paper, terracotta, and sage palette with locally hosted DM Sans and Fraunces.
240
- Its dark palette is a complementary Storybook example; Meal Planner currently
241
- defines only light colors. Action and muted text colors use darker shades for
242
- contrast. This example and its fonts are excluded from the published package. No application integration or registry publication is performed.
243
-
244
- ### shadcn source and updates
245
-
246
- This package owns customized copies of the React 18-compatible New York component
247
- sources and builds them with Tailwind 3.4. `components.json` records aliases, CSS,
248
- style, and prefix settings. Original registry responses, hashes, and MIT license
249
- are preserved in `upstream/shadcn-ui/`; see [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
250
-
251
- To update, review changes against those snapshots and preserve the documented
252
- local adaptations. `node scripts/fetch-shadcn.mjs` refreshes only the upstream
253
- snapshots and manifest; it does not overwrite local components. Upstream CLI
254
- updates may overwrite customizations, so review their diffs before keeping them.
255
-
256
- ### Versioning and publishing
257
-
258
- Use `npm run changeset` for a change description, then `npm run version-packages`
259
- to prepare the version and changelog. Compatible features use minors and fixes
260
- use patches. Breaking prop, token, or export changes need explicit notes and a
261
- minor bump before 1.0, then a major bump after 1.0. CI validates the package and
262
- Storybook. `npm run release` validates and publishes through Changesets using
263
- the maintainer's npm credentials. Publishing requires an explicit release action.
151
+ Storybook runs at [localhost:6006](http://localhost:6006). Run `npm run check`
152
+ for project validation, then `npm run test:browser` for Playwright checks.
153
+
154
+ See [Contributing](CONTRIBUTING.md) for build commands, Netlify hosting, upstream
155
+ sources, and release guidance.
156
+
157
+ ## License
158
+
159
+ [MIT](LICENSE) © 2026 Carlos Contreras. See
160
+ [Third-party notices](THIRD_PARTY_NOTICES.md) for upstream attribution.
@@ -13,7 +13,7 @@ Tailwind 3.4. Radix dependencies remain because they are used by those shadcn
13
13
  components. The upstream Toast is maintained here as the explicit primitive API
14
14
  requested for the initial component set.
15
15
 
16
- Local adaptations: relative imports; `ds-` utility prefixes; a scoped CSS baseline;
16
+ Local adaptations: relative imports; shared semantic color utilities; a scoped CSS baseline;
17
17
  consumer token mapping; optional portal containers; button loading, 1px press feedback from the current Nova style, and a default
18
18
  non-submit type; viewport bounds on dialogs; invalid-field styles; an accessible
19
19
  toast close name; and a distinct indeterminate checkbox icon.
@@ -1,7 +1,7 @@
1
1
  import * as React from "react";
2
2
  import { type VariantProps } from "class-variance-authority";
3
3
  declare const badgeVariants: (props?: ({
4
- variant?: "default" | "destructive" | "outline" | "secondary" | null | undefined;
4
+ variant?: "secondary" | "default" | "destructive" | "outline" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
6
  export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
7
7
  }
@@ -1,7 +1,7 @@
1
1
  import * as React from "react";
2
2
  import { type VariantProps } from "class-variance-authority";
3
3
  declare const buttonVariants: (props?: ({
4
- variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link" | null | undefined;
4
+ variant?: "link" | "secondary" | "default" | "destructive" | "outline" | "ghost" | null | undefined;
5
5
  size?: "default" | "sm" | "lg" | "icon" | null | undefined;
6
6
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
7
  export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {