@troychaplin/parlour-ui 0.1.0 → 0.2.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 (107) hide show
  1. package/README.md +141 -145
  2. package/dist/_shared/client-b7IZMCzB.cjs +1 -0
  3. package/dist/_shared/client-gLQ31wVz.mjs +176 -0
  4. package/dist/_shared/shared-C56WCOfJ.cjs +1 -0
  5. package/dist/_shared/shared-CT4yszFm.mjs +546 -0
  6. package/dist/components/BrandIcon/BrandIcon.d.ts +9 -0
  7. package/dist/components/BrandIcon/BrandIcon.d.ts.map +1 -0
  8. package/dist/components/BrandIcon/brandIcons.d.ts +88 -0
  9. package/dist/components/BrandIcon/brandIcons.d.ts.map +1 -0
  10. package/dist/components/BrandIcon/index.cjs +1 -0
  11. package/dist/components/BrandIcon/index.d.ts +99 -0
  12. package/dist/components/BrandIcon/index.d.ts.map +1 -0
  13. package/dist/components/BrandIcon/index.mjs +2 -0
  14. package/dist/components/Card/Card.d.ts +5 -2
  15. package/dist/components/Card/Card.d.ts.map +1 -1
  16. package/dist/components/Card/index.cjs +1 -1
  17. package/dist/components/Card/index.d.ts +6 -2
  18. package/dist/components/Card/index.mjs +2 -21
  19. package/dist/components/Card/styles.css +123 -26
  20. package/dist/components/Card/styles.scss +123 -26
  21. package/dist/components/CodeBlock/index.cjs +1 -1
  22. package/dist/components/CodeBlock/index.d.ts +1 -0
  23. package/dist/components/CodeBlock/index.mjs +1 -1
  24. package/dist/components/Column/index.cjs +1 -1
  25. package/dist/components/Column/index.mjs +1 -1
  26. package/dist/components/Container/index.d.ts +1 -0
  27. package/dist/components/Container/styles.css +6 -0
  28. package/dist/components/Container/styles.scss +6 -0
  29. package/dist/components/FlexGroup/index.cjs +1 -1
  30. package/dist/components/FlexGroup/index.mjs +1 -1
  31. package/dist/components/GridGroup/index.cjs +1 -1
  32. package/dist/components/GridGroup/index.mjs +1 -1
  33. package/dist/components/Hero/index.cjs +1 -1
  34. package/dist/components/Hero/index.mjs +1 -1
  35. package/dist/components/HeroHeader/styles.css +2 -1
  36. package/dist/components/HeroHeader/styles.scss +2 -1
  37. package/dist/components/InfoBar/index.cjs +1 -1
  38. package/dist/components/InfoBar/index.mjs +1 -1
  39. package/dist/components/Main/Main.d.ts +1 -1
  40. package/dist/components/Main/Main.d.ts.map +1 -1
  41. package/dist/components/Main/index.cjs +1 -1
  42. package/dist/components/Main/index.d.ts +1 -1
  43. package/dist/components/Main/index.mjs +6 -4
  44. package/dist/components/Main/styles.css +4 -0
  45. package/dist/components/Main/styles.scss +4 -0
  46. package/dist/components/ProjectCard/ProjectCard.d.ts +14 -0
  47. package/dist/components/ProjectCard/ProjectCard.d.ts.map +1 -0
  48. package/dist/components/ProjectCard/index.cjs +1 -0
  49. package/dist/components/ProjectCard/index.d.ts +151 -0
  50. package/dist/components/ProjectCard/index.d.ts.map +1 -0
  51. package/dist/components/ProjectCard/index.mjs +51 -0
  52. package/dist/components/ProjectCard/styles.css +95 -0
  53. package/dist/components/ProjectCard/styles.scss +95 -0
  54. package/dist/components/SiteFooter/SiteFooter.d.ts +13 -0
  55. package/dist/components/SiteFooter/SiteFooter.d.ts.map +1 -0
  56. package/dist/components/SiteFooter/index.cjs +1 -0
  57. package/dist/components/SiteFooter/index.d.ts +15 -0
  58. package/dist/components/SiteFooter/index.d.ts.map +1 -0
  59. package/dist/components/SiteFooter/index.mjs +66 -0
  60. package/dist/components/SiteFooter/styles.css +86 -0
  61. package/dist/components/SiteFooter/styles.scss +86 -0
  62. package/dist/components/SiteHeader/index.cjs +1 -1
  63. package/dist/components/SiteHeader/index.mjs +1 -1
  64. package/dist/components/SkipLink/SkipLink.d.ts +8 -0
  65. package/dist/components/SkipLink/SkipLink.d.ts.map +1 -0
  66. package/dist/components/SkipLink/index.cjs +1 -0
  67. package/dist/components/SkipLink/index.d.ts +11 -0
  68. package/dist/components/SkipLink/index.d.ts.map +1 -0
  69. package/dist/components/SkipLink/index.mjs +14 -0
  70. package/dist/components/SkipLink/styles.css +29 -0
  71. package/dist/components/SkipLink/styles.scss +29 -0
  72. package/dist/components/_shared/BrandIcon-CRT5CX33.cjs +1 -0
  73. package/dist/components/_shared/BrandIcon-CcelVr9Y.mjs +111 -0
  74. package/dist/components/_shared/Card-CHHpCLeB.cjs +1 -0
  75. package/dist/components/_shared/Card-HwBpJDVj.mjs +34 -0
  76. package/dist/components/_shared/{FlexGroup-CgPbyEha.cjs → FlexGroup-D9sTJeh4.cjs} +1 -1
  77. package/dist/components/_shared/{FlexGroup-CnOrx2MF.mjs → FlexGroup-DorFgrVL.mjs} +1 -1
  78. package/dist/components/_shared/parlour-tokens-B-n0blwG.cjs +1 -0
  79. package/dist/components/_shared/{parlour-tokens-CHt4gBUd.mjs → parlour-tokens-DpDDQf0L.mjs} +2 -0
  80. package/dist/components/_shared/propClasses-BWhHB1BQ.cjs +1 -0
  81. package/dist/components/_shared/{propClasses-R9s_eOfA.mjs → propClasses-DXjiKI33.mjs} +2 -1
  82. package/dist/components/tokens/index.cjs +1 -1
  83. package/dist/components/tokens/index.mjs +1 -1
  84. package/dist/data/ProjectData.d.ts +3 -0
  85. package/dist/data/ProjectData.d.ts.map +1 -0
  86. package/dist/graphics/parlour-icon.svg +27 -0
  87. package/dist/index.cjs +1 -1
  88. package/dist/index.d.ts +5 -0
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.mjs +3 -3
  91. package/dist/style.css +1 -1
  92. package/dist/styles/c2b/parlour-tokens.d.ts +1 -1
  93. package/dist/styles/parlour-tokens.css +1 -0
  94. package/dist/styles/parlour-tokens.d.ts +1 -1
  95. package/dist/styles/parlour-tokens.js +2 -1
  96. package/dist/styles/parlour-tokens.scss +1 -0
  97. package/dist/utils/propClasses.d.ts +2 -0
  98. package/dist/utils/propClasses.d.ts.map +1 -1
  99. package/dist/wordpress/block-theme.css +1 -0
  100. package/dist/wordpress/hybrid-theme.css +1 -0
  101. package/package.json +1 -1
  102. package/dist/_shared/client-CLAcY4nN.cjs +0 -1
  103. package/dist/_shared/client-CsCPbj42.mjs +0 -93
  104. package/dist/_shared/shared-B07rlYN8.cjs +0 -1
  105. package/dist/_shared/shared-DZTw1JIu.mjs +0 -400
  106. package/dist/components/_shared/parlour-tokens-BT0frVnY.cjs +0 -1
  107. package/dist/components/_shared/propClasses-CLbmpfVJ.cjs +0 -1
package/README.md CHANGED
@@ -1,41 +1,75 @@
1
- # Parlour
1
+ <p align="center">
2
+ <img src="./public/graphics/parlour-icon.svg" alt="Parlour logo: two scoops of ice cream melting over a bowl" width="140" />
3
+ </p>
2
4
 
3
- **A palette cleanser for React and WordPress.**
5
+ <h1 align="center">Parlour</h1>
4
6
 
5
- A token-first UI kit for React and WordPress. One set of tokens, every flavour.
7
+ <p align="center">
8
+ <strong>Scoop. Serve. Repeat.</strong>
9
+ </p>
6
10
 
7
- Formerly Octave Design System.
11
+ <p align="center">
12
+ A token-first UI kit for React and WordPress.<br />
13
+ One set of tokens, every flavour.
14
+ </p>
8
15
 
9
- Browse the components and tokens in the [live Storybook](https://troychaplin.github.io/parlour-ui/).
16
+ <p align="center">
17
+ <a href="https://www.npmjs.com/package/@troychaplin/parlour-ui"><img src="https://img.shields.io/npm/v/@troychaplin/parlour-ui?color=c2185b&label=npm" alt="npm version" /></a>
18
+ <a href="https://troychaplin.github.io/parlour-ui/"><img src="https://img.shields.io/badge/Storybook-live-8fb56a" alt="Live Storybook" /></a>
19
+ </p>
10
20
 
11
- ## Why Parlour
21
+ <p align="center">
22
+ <a href="https://troychaplin.github.io/parlour-ui/"><strong>Browse the Storybook</strong></a> ·
23
+ <a href="#get-a-scoop">Get started</a> ·
24
+ <a href="#serve-it-in-wordpress">WordPress</a> ·
25
+ <a href="CHANGELOG.mdx">Changelog</a>
26
+ </p>
12
27
 
13
- - **One source of truth, two ecosystems.**
14
- [`@troychaplin/component2block`](https://www.npmjs.com/package/@troychaplin/component2block)
15
- compiles `c2b.config.json` into CSS custom properties (`--parlour--*`), SCSS
16
- variables, base element styles, and a WordPress `theme.json` with matching editor
17
- styles. A React app and the WordPress Site Editor render the same design language
18
- because they read the same tokens — no parallel stylesheet to keep in sync by hand.
19
- - **Fluid by default.** All 16 type steps are `clamp()` values that scale with the
20
- viewport, and the spacing scale goes fluid from `small` upward. Responsive
21
- typography without a stack of breakpoints.
22
- - **No runtime, no lock-in.** Plain SCSS and CSS custom properties — no Tailwind, no
23
- CSS-in-JS, no provider to wrap your tree in. Per-component CSS is exported
24
- individually, so a WordPress block can ship only the styles it actually uses.
25
- - **Accessibility is a build failure.** Every Storybook story runs through
26
- [axe](https://github.com/dequelabs/axe-core) at the `error` threshold, on every pull
27
- request and on pre-push. A violation blocks the merge — it isn't a warning someone
28
- triages later.
29
- - **Deliberately small.** Budgeted at 25 kB for the JS bundle and 7 kB for the
30
- stylesheet, brotli-compressed. `pnpm size` checks both.
28
+ ---
31
29
 
32
- > **Status:** Parlour is early-stage and actively being rebuilt. The token pipeline,
33
- > build, and accessibility tooling are solid; the component catalogue is small and
34
- > growing. See the [changelog](CHANGELOG.mdx) for what has landed.
30
+ ## A palette cleanser for React and WordPress
35
31
 
36
- Built with React 18 & 19, TypeScript 6, Vite 8, Storybook 10, and SCSS.
32
+ You've built the same design twice before. Once in React, once in WordPress, and then spent months keeping two stylesheets from drifting apart.
37
33
 
38
- ## Installation
34
+ Parlour ends that. You define your design once, as tokens, and Parlour serves it everywhere: a React component library, a Next.js app, a WordPress block theme, a hybrid theme, and the styles inside your custom blocks. Same colours, same type, same spacing. Not "close enough." The same.
35
+
36
+ ## Scoop. Serve. Repeat.
37
+
38
+ **Scoop.** Take exactly what you need. Use the whole stylesheet, or scoop out a single component's CSS for a WordPress block that only ships the styles it actually uses.
39
+
40
+ **Serve.** One token file becomes CSS custom properties, SCSS variables, base element styles, and a WordPress `theme.json`, all generated together. React apps and the WordPress Site Editor read from the same source, so they always match.
41
+
42
+ **Repeat.** Change a token and every output updates on the next build. No parallel stylesheets, no copy-pasting hex codes, no "why is the button a different blue in WordPress?"
43
+
44
+ ## What's on the menu
45
+
46
+ **One recipe, two kitchens.** Your design tokens live in a single file, `c2b.config.json`. [`@troychaplin/component2block`](https://www.npmjs.com/package/@troychaplin/component2block) compiles it into everything React and WordPress need, so both ecosystems render the same design language without anyone keeping them in sync by hand.
47
+
48
+ **Fluid from the first spoonful.** All 16 type steps are `clamp()` values that scale smoothly with the viewport, and the spacing scale goes fluid from `small` upward. Responsive typography without a stack of breakpoints.
49
+
50
+ **No runtime. No lock-in.** Plain SCSS and CSS custom properties. No Tailwind, no CSS-in-JS, no provider wrapping your whole app. Every component's CSS is published on its own, so you only serve what's on the plate.
51
+
52
+ **Accessibility isn't a topping, it's the base.** Every Storybook story is tested with [axe](https://github.com/dequelabs/axe-core) at the `error` threshold, on every pull request and before every push. An accessibility violation blocks the merge. It's never a warning someone gets to later.
53
+
54
+ **A light dessert.** Budgeted at 25 kB for the JavaScript and 7 kB for the stylesheet, brotli-compressed, and checked on every build with `pnpm size`.
55
+
56
+ **Server Components welcome.** Most components render on the server in the Next.js App Router. Only the few that need React context are client components.
57
+
58
+ **Fresh ingredients.** Built with React 18 and 19, TypeScript 6, Vite 8, Storybook 10, and SCSS.
59
+
60
+ > **Fresh out of the churn.** Parlour is early-stage and moving quickly. The token pipeline, build, and accessibility tooling are solid; the component catalogue is small and growing every release. See the [changelog](CHANGELOG.mdx) for what's landed.
61
+
62
+ ## Coming soon to the counter
63
+
64
+ **Flavours.** Full colour patterns you can switch between, not just light and dark. Pick a flavour and the whole site, in React and WordPress, follows.
65
+
66
+ **Your own recipe.** Enter your own brand variables and let Parlour generate everything else: tokens, base styles, and WordPress theme files.
67
+
68
+ **Saved templates.** Keep your recipes and reuse them across projects, from React apps to WordPress themes and beyond.
69
+
70
+ ---
71
+
72
+ ## Get a scoop
39
73
 
40
74
  ```bash
41
75
  npm install @troychaplin/parlour-ui
@@ -43,7 +77,7 @@ npm install @troychaplin/parlour-ui
43
77
 
44
78
  React and React DOM 18 or 19 are peer dependencies.
45
79
 
46
- ### React apps
80
+ ### Serve it in React
47
81
 
48
82
  Import the stylesheet once, where your app starts:
49
83
 
@@ -51,7 +85,7 @@ Import the stylesheet once, where your app starts:
51
85
  import '@troychaplin/parlour-ui/styles.css';
52
86
  ```
53
87
 
54
- Then use the components:
88
+ Then build with the components:
55
89
 
56
90
  ```tsx
57
91
  import { Button, Container, Main } from '@troychaplin/parlour-ui';
@@ -68,16 +102,11 @@ export function Page() {
68
102
  }
69
103
  ```
70
104
 
71
- `styles.css` has everything: the design tokens, base element styles, layout classes, every
72
- component's CSS, and the `@font-face` rules for Inter Tight, Source Serif 4 and JetBrains Mono.
73
- The font files ship with the package, and your bundler picks them up from the stylesheet. The
74
- components don't import any CSS themselves, so this import is required.
105
+ `styles.css` is the full sundae: design tokens, base element styles, layout classes, every component's CSS, and the `@font-face` rules for Inter Tight, Source Serif 4, and JetBrains Mono. The font files ship with the package and your bundler picks them up automatically. Components don't import their own CSS, so this one import is required.
75
106
 
76
107
  #### Router links
77
108
 
78
- `Button` with an `href`, and `SiteHeader`, render plain `<a>` links unless you wrap the app in
79
- `LinkProvider` with your router's link component. That component receives standard anchor props,
80
- including `href`:
109
+ `Button` with an `href`, and `SiteHeader`, render plain `<a>` links unless you wrap your app in `LinkProvider` with your router's link component. That component receives standard anchor props, including `href`:
81
110
 
82
111
  ```tsx
83
112
  import { LinkProvider } from '@troychaplin/parlour-ui';
@@ -87,15 +116,11 @@ import { LinkProvider } from '@troychaplin/parlour-ui';
87
116
  </LinkProvider>;
88
117
  ```
89
118
 
90
- Next.js's `Link` takes `href` as it is. React Router's `Link` expects `to`, so pass a wrapper
91
- instead: `({ href = '', ...props }) => <Link to={href} {...props} />`.
119
+ Next.js's `Link` takes `href` as-is. React Router's `Link` expects `to`, so pass a small wrapper instead: `({ href = '', ...props }) => <Link to={href} {...props} />`.
92
120
 
93
- #### Next.js App Router
121
+ ### Serve it in Next.js
94
122
 
95
- Parlour's components work in Server Components. `Button`, `SiteHeader` and `LinkProvider` use
96
- React context, so they're marked `'use client'` and render as Client Components; every other
97
- component renders on the server. A Server Component can't pass a function such as Next.js's `Link`
98
- to a Client Component, so set up `LinkProvider` in a client providers file:
123
+ Parlour works in Server Components. `Button`, `SiteHeader`, and `LinkProvider` use React context, so they're marked `'use client'`; everything else renders on the server. Since a Server Component can't pass a function like Next.js's `Link` to a Client Component, set up `LinkProvider` in a client providers file:
99
124
 
100
125
  ```tsx
101
126
  // app/providers.tsx
@@ -125,49 +150,21 @@ export default function RootLayout({ children }: { children: React.ReactNode })
125
150
  }
126
151
  ```
127
152
 
128
- With the Pages Router, import the stylesheet and add `LinkProvider` in `pages/_app.tsx`.
153
+ Using the Pages Router? Import the stylesheet and add `LinkProvider` in `pages/_app.tsx`.
129
154
 
130
- ### What the package ships
155
+ ### Serve it in WordPress
131
156
 
132
- Every path below starts with `@troychaplin/parlour-ui`.
157
+ `dist/wordpress/` has everything a theme needs:
133
158
 
134
- | Path | Contents |
135
- | --------------------------------------- | ---------------------------------------------------------------------------------------- |
136
- | _(package root)_ | Every React component, as ES modules and CommonJS, with TypeScript types |
137
- | `/styles.css` | The full stylesheet: tokens, base styles, layout classes, every component, and the fonts |
138
- | `/tokens.css` | The design tokens as `--parlour--*` custom properties |
139
- | `/base-styles.css` | Element defaults for the body, headings, links and buttons |
140
- | `/layout.css` | Layout classes that match WordPress's, such as `is-layout-constrained` |
141
- | `/typography.css` | Spacing after headings |
142
- | `/fonts.css` | `@font-face` rules for the bundled fonts |
143
- | `/tokens` | The token names as a JavaScript object, `parlourTokens` |
144
- | `/<Component>` | One component's JavaScript, such as `/Button` |
145
- | `/<Component>.css` | One component's CSS, such as `/Button.css` |
146
- | `/block-theme.css`, `/hybrid-theme.css` | Stylesheets for WordPress themes |
147
-
148
- The WordPress theme files, `integrate.php` and `theme-parlour.json`, are in `dist/wordpress/`, and
149
- the font files are in `dist/fonts/`. Copy those by path.
150
-
151
- ### WordPress themes
152
-
153
- `dist/wordpress/` has what a theme needs:
154
-
155
- - `theme-parlour.json` — the tokens as `theme.json` presets: the color palette and gradient, 16
156
- fluid font sizes, the three font families with their font files, the spacing scale, shadows,
157
- and the content and wide widths. It also sets default styles for text, headings, links, buttons
158
- and captions.
159
- - `integrate.php` — loads `theme-parlour.json` as WordPress's default `theme.json` layer, so your
160
- theme's own `theme.json` only needs to hold overrides.
161
- - `block-theme.css` — for block themes: the tokens, the `.sr-only` utility and heading spacing.
162
- - `hybrid-theme.css` — for hybrid themes with PHP templates: adds Parlour's element defaults,
163
- layout classes and global styles.
159
+ - **`theme-parlour.json`** turns the tokens into `theme.json` presets: the colour palette and gradient, 16 fluid font sizes, the three font families with their font files, the spacing scale, shadows, and content and wide widths. It also sets default styles for text, headings, links, buttons, and captions.
160
+ - **`integrate.php`** loads `theme-parlour.json` as WordPress's default `theme.json` layer, so your theme's own `theme.json` only needs to hold overrides.
161
+ - **`block-theme.css`** is for block themes: the tokens, the `.sr-only` utility, and heading spacing.
162
+ - **`hybrid-theme.css`** is for hybrid themes with PHP templates, adding Parlour's element defaults, layout classes, and global styles.
164
163
 
165
164
  To set up a theme:
166
165
 
167
- 1. Copy `integrate.php`, `theme-parlour.json` and the stylesheet you need into one folder in your
168
- theme, such as `assets/parlour/`. `integrate.php` looks for `theme-parlour.json` next to itself.
169
- 2. Copy `dist/fonts/` to `assets/fonts/` in your theme, the path `theme-parlour.json` gives for the
170
- font files.
166
+ 1. Copy `integrate.php`, `theme-parlour.json`, and the stylesheet you need into one folder in your theme, such as `assets/parlour/`. `integrate.php` looks for `theme-parlour.json` next to itself.
167
+ 2. Copy `dist/fonts/` to `assets/fonts/` in your theme, which is the path `theme-parlour.json` uses for the font files.
171
168
  3. Load everything from `functions.php`:
172
169
 
173
170
  ```php
@@ -184,18 +181,13 @@ add_action( 'after_setup_theme', function () {
184
181
 
185
182
  For a hybrid theme, use `hybrid-theme.css` in both places.
186
183
 
187
- Parlour is built locked, so `integrate.php` also stops the theme from changing the layout widths
188
- and breakpoints, and turns off custom colors, gradients and duotone in the Site Editor.
184
+ Parlour is built locked, so `integrate.php` also stops the theme from changing layout widths and breakpoints, and turns off custom colours, gradients, and duotone in the Site Editor. Your design stays on-brand, no matter who's editing.
189
185
 
190
- ### WordPress blocks
186
+ ### Serve it in custom blocks
191
187
 
192
- Each component's CSS is also published on its own, so a block can bundle just the styles for the
193
- markup it renders. Component CSS reads the `--parlour--*` custom properties, so the page needs the
194
- tokens too: a theme set up as above already loads them, and otherwise the block should bundle
195
- `tokens.css`.
188
+ Every component's CSS is published on its own, so a block can bundle just the styles for the markup it renders. Component CSS reads the `--parlour--*` custom properties, so the page needs the tokens too: a theme set up as above already loads them; otherwise, bundle `tokens.css` with the block.
196
189
 
197
- With `@wordpress/scripts`, import them at the top of the block's `style.scss`, which loads in the
198
- editor and on the front end:
190
+ With `@wordpress/scripts`, import them at the top of the block's `style.scss`, which loads in both the editor and on the front end:
199
191
 
200
192
  ```scss
201
193
  // src/my-block/style.scss
@@ -203,36 +195,49 @@ editor and on the front end:
203
195
  @import '@troychaplin/parlour-ui/Button.css';
204
196
  ```
205
197
 
206
- ## Design tokens
198
+ ## What comes in the box
207
199
 
208
- Every CSS variable uses the `--parlour--` prefix (double dash).
200
+ Every path below starts with `@troychaplin/parlour-ui`.
201
+
202
+ | Path | Contents |
203
+ | --------------------------------------- | ---------------------------------------------------------------------------------------- |
204
+ | _(package root)_ | Every React component, as ES modules and CommonJS, with TypeScript types |
205
+ | `/styles.css` | The full stylesheet: tokens, base styles, layout classes, every component, and the fonts |
206
+ | `/tokens.css` | The design tokens as `--parlour--*` custom properties |
207
+ | `/base-styles.css` | Element defaults for the body, headings, links, and buttons |
208
+ | `/layout.css` | Layout classes that match WordPress's, such as `is-layout-constrained` |
209
+ | `/typography.css` | Spacing after headings |
210
+ | `/fonts.css` | `@font-face` rules for the bundled fonts |
211
+ | `/tokens` | The token names as a JavaScript object, `parlourTokens` |
212
+ | `/<Component>` | One component's JavaScript, such as `/Button` |
213
+ | `/<Component>.css` | One component's CSS, such as `/Button.css` |
214
+ | `/block-theme.css`, `/hybrid-theme.css` | Stylesheets for WordPress themes |
215
+
216
+ The WordPress theme files (`integrate.php` and `theme-parlour.json`) are in `dist/wordpress/`, and the font files are in `dist/fonts/`. Copy those by path.
209
217
 
210
- | Category | What's in it |
211
- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
212
- | **Colors** | `accent-primary` and `accent-secondary`, an 11-step neutral ramp (`neutral-50` → `neutral-950`), semantic states (`info`, `success`, `warning`, `error`), and brand colors for seven social platforms |
213
- | **Typography** | Three families (Inter Tight, Source Serif 4, JetBrains Mono), 16 fluid size steps, nine weights, three line heights |
214
- | **Spacing** | A 12-step scale from `3-x-small` to `5-x-large`, fluid from `small` upward |
215
- | **Effects** | Five shadow presets (natural, deep, sharp, outlined, crisp), five border radii, and a gradient |
216
- | **Layout** | Content width (768px), wide width (1152px), small (600px), and full-bleed |
218
+ ## The ingredients
219
+
220
+ Every CSS variable uses the `--parlour--` prefix (double dash).
217
221
 
218
- Browse them live in Storybook under **Stylebook**.
222
+ | Category | What's in it |
223
+ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
224
+ | **Colours** | `accent-primary` and `accent-secondary`, an 11-step neutral ramp (`neutral-50` → `neutral-950`), semantic states (`info`, `success`, `warning`, `error`), and brand colours for seven social platforms |
225
+ | **Typography** | Three families (Inter Tight, Source Serif 4, JetBrains Mono), 16 fluid size steps, nine weights, three line heights |
226
+ | **Spacing** | A 12-step scale from `3-x-small` to `5-x-large`, fluid from `small` upward |
227
+ | **Effects** | Five shadow presets (natural, deep, sharp, outlined, crisp), five border radii, and a gradient |
228
+ | **Layout** | Content width (768px), wide width (1152px), small (600px), and full-bleed |
219
229
 
220
- ## Accessibility
230
+ Taste them all live in the Storybook under **Stylebook**.
221
231
 
222
- Every Storybook story is tested against axe with `test: 'error'` — stories
223
- that fail accessibility checks will not pass CI.
232
+ ## The menu (documentation)
224
233
 
225
- ## Documentation
234
+ The [live Storybook](https://troychaplin.github.io/parlour-ui/) is the full menu. **Getting Started** covers installation and framework setup, **Stylebook** has live token demos, and every component has its own docs page with props, CSS classes, tokens, and accessibility notes.
226
235
 
227
- The [live Storybook](https://troychaplin.github.io/parlour-ui/) is the reference:
228
- **Getting Started** covers installation and framework integration, **Stylebook** has live token
229
- demos, and every component ships its own docs page with props, CSS classes, tokens, and
230
- accessibility notes.
236
+ ---
231
237
 
232
- Contributor conventions — how to build a component's TSX, styles, stories and docs — are in
233
- [`AGENTS.md`](AGENTS.md) and the [`build-component` skill](.claude/skills/build-component/SKILL.md).
238
+ ## Behind the counter
234
239
 
235
- ## Developing Parlour
240
+ Want to help make Parlour? Contributor conventions (how to build a component's TSX, styles, stories, and docs) are in [`AGENTS.md`](AGENTS.md) and the [`build-component` skill](.claude/skills/build-component/SKILL.md).
236
241
 
237
242
  ```bash
238
243
  # Requires Node 24+ (see .nvmrc)
@@ -250,10 +255,10 @@ pnpm run dev # Storybook at http://localhost:6006
250
255
 
251
256
  | Command | Description |
252
257
  | ---------------------------- | --------------------------------------------------------------------------------------------------- |
253
- | `pnpm run dev` | Run `c2b generate` then start Storybook dev server |
258
+ | `pnpm run dev` | Run `c2b generate` then start the Storybook dev server |
254
259
  | `pnpm run build` | Library build: ESM and CommonJS bundles, per-component files, types, tokens and the WordPress files |
255
260
  | `pnpm run build-storybook` | Build static Storybook for deployment |
256
- | `pnpm run c2b` | Regenerate tokens, base styles, and WP theme files |
261
+ | `pnpm run c2b` | Regenerate tokens, base styles, and WordPress theme files |
257
262
  | `pnpm run typecheck` | TypeScript type checking (no emit) |
258
263
  | `pnpm run lint` | ESLint across `src/` |
259
264
  | `pnpm run lint:fix` | ESLint with auto-fix |
@@ -266,33 +271,18 @@ pnpm run dev # Storybook at http://localhost:6006
266
271
  | `pnpm run size` | Check the JS bundle and stylesheet against their size budgets |
267
272
  | `pnpm run release <version>` | Changelog, version bump, commit and tag on main. Push the tag to publish to npm |
268
273
 
269
- ### Formatting & code style
270
-
271
- Formatting is enforced automatically so contributors don't create diffs based on
272
- personal editor preferences:
273
-
274
- - **EditorConfig** (`.editorconfig`) sets base whitespace rules (4-space indent, LF
275
- line endings, trimmed trailing whitespace) recognized by most editors.
276
- - **Prettier** (`.prettierrc`) formats TS/TSX/SCSS in `src/`, and JSON, Markdown and MDX
277
- files when they're committed. `.prettierignore` excludes build output and generated files
278
- (`dist/`, `storybook-static/`, `src/styles/c2b/`, etc.).
279
- - **ESLint** (`eslint.config.mjs`) lints `src/` for code quality (React, hooks,
280
- jsx-a11y, Storybook rules), with `eslint-config-prettier` disabling any
281
- formatting rules that could conflict with Prettier.
282
- - **VSCode workspace settings** (`.vscode/settings.json`, committed to the repo)
283
- set Prettier as the default formatter with format-on-save and ESLint
284
- auto-fix-on-save enabled. `.vscode/extensions.json` recommends the Prettier,
285
- ESLint, and EditorConfig extensions so VSCode prompts new contributors to
286
- install them.
274
+ ### Keeping the kitchen clean
275
+
276
+ Formatting is enforced automatically, so nobody's diffs are about personal editor preferences:
277
+
278
+ - **EditorConfig** (`.editorconfig`) sets base whitespace rules: 4-space indent, LF line endings, and trimmed trailing whitespace.
279
+ - **Prettier** (`.prettierrc`) formats TS, TSX, and SCSS in `src/`, plus JSON, Markdown, and MDX files when they're committed. `.prettierignore` excludes build output and generated files (`dist/`, `storybook-static/`, `src/styles/c2b/`, and so on).
280
+ - **ESLint** (`eslint.config.mjs`) lints `src/` for code quality (React, hooks, jsx-a11y, and Storybook rules), with `eslint-config-prettier` turning off any formatting rules that could conflict with Prettier.
281
+ - **VS Code workspace settings** (`.vscode/settings.json`) set Prettier as the default formatter with format-on-save and ESLint auto-fix-on-save. `.vscode/extensions.json` recommends the Prettier, ESLint, and EditorConfig extensions to new contributors.
287
282
  - **`.gitattributes`** normalizes line endings to LF across operating systems.
288
- - **Husky + lint-staged**: on every commit, `.husky/pre-commit` runs
289
- `lint-staged` first, which formats and auto-fixes only the files staged in
290
- that commit (Prettier for TS/TSX/SCSS/JSON/MD/MDX, ESLint `--fix` for TS/TSX).
291
- It then runs the full `pnpm lint` and `pnpm typecheck` as a final check
292
- across the whole project. `.husky/pre-push` runs `pnpm test:storybook`.
283
+ - **Husky and lint-staged** run on every commit: `lint-staged` formats and auto-fixes only the staged files, then the full `pnpm lint` and `pnpm typecheck` run as a final check. `.husky/pre-push` runs `pnpm test:storybook`.
293
284
 
294
- Run `pnpm format` to format everything in `src/`, or `pnpm format:check` to verify formatting
295
- without writing changes.
285
+ Run `pnpm format` to format everything in `src/`, or `pnpm format:check` to check formatting without writing changes.
296
286
 
297
287
  ### Project structure
298
288
 
@@ -304,7 +294,7 @@ src/
304
294
  tokens/ # Runtime token export (parlourTokens)
305
295
  docs/ # Storybook documentation pages
306
296
  getting-started/ # Install and framework integration guides
307
- stylebook/ # Live token demos (colors, typography, spacing, effects)
297
+ stylebook/ # Live token demos (colours, typography, spacing, effects)
308
298
  styles/
309
299
  main.scss # Consumer-facing stylesheet entry
310
300
  base/ # Hand-authored globals (focus, selection, utilities)
@@ -320,9 +310,15 @@ dist/
320
310
  fonts/ # Font files used by styles.css and theme-parlour.json
321
311
  ```
322
312
 
323
- ## Links
313
+ ---
314
+
315
+ <p align="center">
316
+ <a href="https://troychaplin.github.io/parlour-ui/">Storybook</a> ·
317
+ <a href="https://www.npmjs.com/package/@troychaplin/parlour-ui">npm</a> ·
318
+ <a href="https://github.com/troychaplin/parlour-ui">Source</a> ·
319
+ <a href="CHANGELOG.mdx">Changelog</a>
320
+ </p>
324
321
 
325
- - [Storybook](https://troychaplin.github.io/parlour-ui/)
326
- - [npm](https://www.npmjs.com/package/@troychaplin/parlour-ui)
327
- - [Source](https://github.com/troychaplin/parlour-ui)
328
- - [Changelog](CHANGELOG.mdx)
322
+ <p align="center">
323
+ <strong>Scoop. Serve. Repeat.</strong> 🍨
324
+ </p>
@@ -0,0 +1 @@
1
+ "use client";const e=require("./rolldown-runtime-BocRIvOZ.cjs"),t=require("./shared-C56WCOfJ.cjs");let n=require("react");n=e.t(n,1);let r=require("react/jsx-runtime");var i=(0,n.createContext)(e=>(0,r.jsx)(`a`,{...e})),a=()=>(0,n.useContext)(i),o=({text:e=`Button`,color:t=`medium`,rounded:n=`sm`,type:i=`button`,isSmall:o=!1,isFull:s=!1,isOutline:c=!1,disabled:l=!1,className:u,...d})=>{let f=a(),p=[`parlour-button`,l?`parlour-button--disabled`:`parlour-button--${t}`,n&&`parlour-button--radius-${n}`,c&&!l&&`parlour-button--outline`,o&&`parlour-button--small`,s&&`parlour-button--full`,u].filter(Boolean).join(` `);return d.href===void 0?(0,r.jsx)(`button`,{type:i,className:p,disabled:l,...d,children:e}):(0,r.jsx)(f,{className:p,...d,children:e})},s=({component:e,children:t})=>(0,r.jsx)(i.Provider,{value:e,children:t}),c=[{href:`/about`,label:`About`},{href:`/blog`,label:`Blog`},{href:`/resume`,label:`Resume`}],l=(e,t)=>{let n=t?e.indexOf(t):-1;return!t||n===-1?[e]:[e.slice(0,n),t,e.slice(n+t.length)]},u=({className:e=``,children:n,siteTitle:i=`Parlour`,siteTitleAccent:s})=>{let u=[`parlour-site-header`,e].filter(Boolean).join(` `),d=a(),[f,p,m]=l(i,s);return(0,r.jsx)(`header`,{className:u,children:(0,r.jsxs)(t.h,{gap:`normal`,maxWidth:`alignwide`,justify:`space-between`,align:`center`,children:[(0,r.jsx)(`div`,{className:`parlour-site-header__branding`,children:(0,r.jsx)(`p`,{children:(0,r.jsxs)(d,{href:`/`,rel:`home`,children:[f,p&&(0,r.jsx)(`span`,{className:`parlour-site-header__title-accent`,children:p}),m]})})}),(0,r.jsx)(`nav`,{className:`parlour-site-header__nav`,"aria-label":`Primary`,children:(0,r.jsx)(`ul`,{children:c.map(({href:e,label:t})=>(0,r.jsx)(`li`,{children:(0,r.jsx)(d,{href:e,children:t})},e))})}),(0,r.jsx)(`div`,{children:(0,r.jsx)(t.m,{children:(0,r.jsx)(o,{text:`Hire Me`,isSmall:!0,isOutline:!0})})}),n]})})},d=[`github`,`wordpress`,`x`,`bluesky`,`linkedin`,`facebook`,`instagram`,`youtube`,`tiktok`],f=({name:e,nameHref:n,year:i=new Date().getFullYear(),text:o,className:s,github:c,wordpress:l,x:u,bluesky:f,linkedin:p,facebook:m,instagram:h,youtube:g,tiktok:_,...v})=>{let y=a(),b=[`parlour-site-footer`,s].filter(Boolean).join(` `),x={github:c,wordpress:l,x:u,bluesky:f,linkedin:p,facebook:m,instagram:h,youtube:g,tiktok:_},S=d.filter(e=>x[e]);return(0,r.jsx)(`footer`,{className:b,...v,children:(0,r.jsxs)(`div`,{className:`parlour-site-footer__inner`,children:[(0,r.jsxs)(`p`,{className:`parlour-site-footer__copyright`,children:[`© `,i,` `,n?(0,r.jsx)(y,{href:n,children:e}):e]}),o&&(0,r.jsx)(`p`,{className:`parlour-site-footer__text`,children:o}),S.length>0&&(0,r.jsx)(`ul`,{className:`parlour-site-footer__social`,children:S.map(e=>(0,r.jsx)(`li`,{children:(0,r.jsx)(y,{className:`parlour-site-footer__social-link`,href:x[e],rel:`me`,"aria-label":t.p[e].title,children:(0,r.jsx)(t.f,{name:e})})},e))})]})})},p=({children:e,as:n=`div`,backgroundColor:i=`light`,border:o=!0,borderColor:s=`light`,borderRadius:c=`none`,paddingInline:l=`x-large`,paddingBlock:u=`large`,href:d,linkProps:f,className:p,...m})=>{let h=n,g=a(),_=[`parlour-card`,`parlour-card--bg-${t.g[i]}`,o&&`parlour-card--border`,o&&`parlour-card--border-${t.g[s]}`,`parlour-card--radius-${t._[c]}`,`parlour-card--padding-inline-${t.v[l]}`,`parlour-card--padding-block-${t.v[u]}`,d&&`parlour-card--linked`,p].filter(Boolean).join(` `);return d?(0,r.jsx)(h,{className:_,...m,children:(0,r.jsx)(g,{className:`parlour-card__link`,href:d,...f,children:e})}):(0,r.jsx)(h,{className:_,...m,children:e})};Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return p}});
@@ -0,0 +1,176 @@
1
+ "use client";
2
+ import { _ as e, f as t, g as n, h as r, m as i, p as a, v as o } from "./shared-CT4yszFm.mjs";
3
+ import { createContext as s, useContext as c } from "react";
4
+ import { jsx as l, jsxs as u } from "react/jsx-runtime";
5
+ var d = s((e) => /* @__PURE__ */ l("a", { ...e })), f = () => c(d), p = ({ text: e = "Button", color: t = "medium", rounded: n = "sm", type: r = "button", isSmall: i = !1, isFull: a = !1, isOutline: o = !1, disabled: s = !1, className: c, ...u }) => {
6
+ let d = f(), p = [
7
+ "parlour-button",
8
+ s ? "parlour-button--disabled" : `parlour-button--${t}`,
9
+ n && `parlour-button--radius-${n}`,
10
+ o && !s && "parlour-button--outline",
11
+ i && "parlour-button--small",
12
+ a && "parlour-button--full",
13
+ c
14
+ ].filter(Boolean).join(" ");
15
+ return u.href === void 0 ? /* @__PURE__ */ l("button", {
16
+ type: r,
17
+ className: p,
18
+ disabled: s,
19
+ ...u,
20
+ children: e
21
+ }) : /* @__PURE__ */ l(d, {
22
+ className: p,
23
+ ...u,
24
+ children: e
25
+ });
26
+ }, m = ({ component: e, children: t }) => /* @__PURE__ */ l(d.Provider, {
27
+ value: e,
28
+ children: t
29
+ }), h = [
30
+ {
31
+ href: "/about",
32
+ label: "About"
33
+ },
34
+ {
35
+ href: "/blog",
36
+ label: "Blog"
37
+ },
38
+ {
39
+ href: "/resume",
40
+ label: "Resume"
41
+ }
42
+ ], g = (e, t) => {
43
+ let n = t ? e.indexOf(t) : -1;
44
+ return !t || n === -1 ? [e] : [
45
+ e.slice(0, n),
46
+ t,
47
+ e.slice(n + t.length)
48
+ ];
49
+ }, _ = ({ className: e = "", children: t, siteTitle: n = "Parlour", siteTitleAccent: a }) => {
50
+ let o = ["parlour-site-header", e].filter(Boolean).join(" "), s = f(), [c, d, m] = g(n, a);
51
+ return /* @__PURE__ */ l("header", {
52
+ className: o,
53
+ children: /* @__PURE__ */ u(r, {
54
+ gap: "normal",
55
+ maxWidth: "alignwide",
56
+ justify: "space-between",
57
+ align: "center",
58
+ children: [
59
+ /* @__PURE__ */ l("div", {
60
+ className: "parlour-site-header__branding",
61
+ children: /* @__PURE__ */ l("p", { children: /* @__PURE__ */ u(s, {
62
+ href: "/",
63
+ rel: "home",
64
+ children: [
65
+ c,
66
+ d && /* @__PURE__ */ l("span", {
67
+ className: "parlour-site-header__title-accent",
68
+ children: d
69
+ }),
70
+ m
71
+ ]
72
+ }) })
73
+ }),
74
+ /* @__PURE__ */ l("nav", {
75
+ className: "parlour-site-header__nav",
76
+ "aria-label": "Primary",
77
+ children: /* @__PURE__ */ l("ul", { children: h.map(({ href: e, label: t }) => /* @__PURE__ */ l("li", { children: /* @__PURE__ */ l(s, {
78
+ href: e,
79
+ children: t
80
+ }) }, e)) })
81
+ }),
82
+ /* @__PURE__ */ l("div", { children: /* @__PURE__ */ l(i, { children: /* @__PURE__ */ l(p, {
83
+ text: "Hire Me",
84
+ isSmall: !0,
85
+ isOutline: !0
86
+ }) }) }),
87
+ t
88
+ ]
89
+ })
90
+ });
91
+ }, v = [
92
+ "github",
93
+ "wordpress",
94
+ "x",
95
+ "bluesky",
96
+ "linkedin",
97
+ "facebook",
98
+ "instagram",
99
+ "youtube",
100
+ "tiktok"
101
+ ], y = ({ name: e, nameHref: n, year: r = (/* @__PURE__ */ new Date()).getFullYear(), text: i, className: o, github: s, wordpress: c, x: d, bluesky: p, linkedin: m, facebook: h, instagram: g, youtube: _, tiktok: y, ...b }) => {
102
+ let x = f(), S = ["parlour-site-footer", o].filter(Boolean).join(" "), C = {
103
+ github: s,
104
+ wordpress: c,
105
+ x: d,
106
+ bluesky: p,
107
+ linkedin: m,
108
+ facebook: h,
109
+ instagram: g,
110
+ youtube: _,
111
+ tiktok: y
112
+ }, w = v.filter((e) => C[e]);
113
+ return /* @__PURE__ */ l("footer", {
114
+ className: S,
115
+ ...b,
116
+ children: /* @__PURE__ */ u("div", {
117
+ className: "parlour-site-footer__inner",
118
+ children: [
119
+ /* @__PURE__ */ u("p", {
120
+ className: "parlour-site-footer__copyright",
121
+ children: [
122
+ "© ",
123
+ r,
124
+ " ",
125
+ n ? /* @__PURE__ */ l(x, {
126
+ href: n,
127
+ children: e
128
+ }) : e
129
+ ]
130
+ }),
131
+ i && /* @__PURE__ */ l("p", {
132
+ className: "parlour-site-footer__text",
133
+ children: i
134
+ }),
135
+ w.length > 0 && /* @__PURE__ */ l("ul", {
136
+ className: "parlour-site-footer__social",
137
+ children: w.map((e) => /* @__PURE__ */ l("li", { children: /* @__PURE__ */ l(x, {
138
+ className: "parlour-site-footer__social-link",
139
+ href: C[e],
140
+ rel: "me",
141
+ "aria-label": a[e].title,
142
+ children: /* @__PURE__ */ l(t, { name: e })
143
+ }) }, e))
144
+ })
145
+ ]
146
+ })
147
+ });
148
+ }, b = ({ children: t, as: r = "div", backgroundColor: i = "light", border: a = !0, borderColor: s = "light", borderRadius: c = "none", paddingInline: u = "x-large", paddingBlock: d = "large", href: p, linkProps: m, className: h, ...g }) => {
149
+ let _ = r, v = f(), y = [
150
+ "parlour-card",
151
+ `parlour-card--bg-${n[i]}`,
152
+ a && "parlour-card--border",
153
+ a && `parlour-card--border-${n[s]}`,
154
+ `parlour-card--radius-${e[c]}`,
155
+ `parlour-card--padding-inline-${o[u]}`,
156
+ `parlour-card--padding-block-${o[d]}`,
157
+ p && "parlour-card--linked",
158
+ h
159
+ ].filter(Boolean).join(" ");
160
+ return p ? /* @__PURE__ */ l(_, {
161
+ className: y,
162
+ ...g,
163
+ children: /* @__PURE__ */ l(v, {
164
+ className: "parlour-card__link",
165
+ href: p,
166
+ ...m,
167
+ children: t
168
+ })
169
+ }) : /* @__PURE__ */ l(_, {
170
+ className: y,
171
+ ...g,
172
+ children: t
173
+ });
174
+ };
175
+ //#endregion
176
+ export { p as a, m as i, y as n, _ as r, b as t };