feat(web): adopt the cocotte design system (P-1 foundation)
Some checks are pending
CI / verify (push) Waiting to run

First consumer of @cocotte/ui-theme. Wires the brand theme and the styled-components
runtime so views can migrate off styles.css.

- .npmrc: scope @cocotte/@lilith → ct-forge verdaccio (CI-resolvable)
- deps: styled-components@^6.3.8 (aligned to @lilith/ui-styled-components' copy),
  @cocotte/ui-theme, ui-icons, ui-fab, ui-zname, site-themes@^0.1.1
- main.tsx: ThemeProvider themeName="luxe" + site-themes luxeDarkTheme customTheme
  (cocotte brand; lilith was the previous brand) + cssVariables + GlobalScrollbarStyles
- vite: resolve.dedupe styled-components/react/react-dom — without this the
  ThemeProvider and styled components resolve different ThemeContexts (theme undefined)

typecheck + build green; app renders clean (no console errors). Existing views still
use styles.css (emerald) until the per-view migration in P1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Natalie 2026-06-29 19:45:23 -04:00
parent d18b47fa32
commit a7c398fca6
5 changed files with 378 additions and 7 deletions

8
.npmrc Normal file
View file

@ -0,0 +1,8 @@
# Scoped registry routing for the cocotte/lilith design system.
# @cocotte/ui-* (theme, icons, fab, zname) and their @lilith/* deps are published
# to the ct-forge Verdaccio. Default registry stays npmjs.
# Auth (_authToken for 134.199.243.61:4873) comes from user-level ~/.npmrc in dev
# and from CI env (NPM_TOKEN) on the forge runners.
registry=https://registry.npmjs.org/
@cocotte:registry=http://134.199.243.61:4873/
@lilith:registry=http://134.199.243.61:4873/

352
package-lock.json generated
View file

@ -435,6 +435,72 @@
"url": "https://github.com/sponsors/Borewit"
}
},
"node_modules/@cocotte/site-themes": {
"version": "0.1.1",
"resolved": "http://134.199.243.61:4873/@cocotte/site-themes/-/site-themes-0.1.1.tgz",
"integrity": "sha512-JQR9gIy3yZJtvxzXbam4VScInegDLfT75IYV0czgUHwJRfcGZVKEIsV4gHib86WTK2+aCBlren4uO9FGr9FKZQ==",
"dependencies": {
"@cocotte/ui-theme": "^1.5.2"
},
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0"
}
},
"node_modules/@cocotte/ui-fab": {
"version": "2.3.10",
"resolved": "http://134.199.243.61:4873/@cocotte/ui-fab/-/ui-fab-2.3.10.tgz",
"integrity": "sha512-UFdoYa9eTGsrsYWb88QyLTBUfWhrTaWC4n5joLNkMA+ptKw5zy75v+2u0tsZd5lQR2UMug8GVYBx/PocDticsA==",
"license": "UNLICENSED",
"dependencies": {
"@cocotte/ui-zname": "^1.2.5",
"@lilith/ui-design-tokens": "^1.2.1",
"@lilith/ui-motion": "^2.2.0",
"@lilith/ui-styled-components": "^6.3.9"
},
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0",
"styled-components": "^6.0.0"
}
},
"node_modules/@cocotte/ui-icons": {
"version": "1.2.3",
"resolved": "http://134.199.243.61:4873/@cocotte/ui-icons/-/ui-icons-1.2.3.tgz",
"integrity": "sha512-mbfKon8tpQ5W4hbLWQ263NTIe0MpBw/TSOH2Egu/AMgQepbQa8O3fkXFNlQP9nif9j/hdWlkmAp5SQscf+xtlw==",
"license": "UNLICENSED",
"dependencies": {
"@cocotte/ui-theme": "^1.5.2",
"@lilith/ui-accessibility": "^1.2.2",
"@lilith/ui-styled-components": "^6.3.9"
},
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0",
"styled-components": "^6.0.0"
}
},
"node_modules/@cocotte/ui-theme": {
"version": "1.5.2",
"resolved": "http://134.199.243.61:4873/@cocotte/ui-theme/-/ui-theme-1.5.2.tgz",
"integrity": "sha512-6lwkBH+02slFtoWRapoLT8fal1rFBfN0ufa/sccGrGJ7Y8ahD8mtWMNq0yCsYSxM+VneNi/NBXnDlOGDTyyLOQ==",
"dependencies": {
"@lilith/ui-design-tokens": "^1.2.1",
"@lilith/ui-styled-components": "^6.3.9"
},
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0",
"styled-components": "^6.0.0"
}
},
"node_modules/@cocotte/ui-zname": {
"version": "1.2.5",
"resolved": "http://134.199.243.61:4873/@cocotte/ui-zname/-/ui-zname-1.2.5.tgz",
"integrity": "sha512-bsxXfHNDNsCDijc1/82lEXh8Rlr1qvMyxQbxJ/tMkvd3tkX+ix1oOjxOoxWfV549ORAY+kpRgHAJK8Ic1SBouw==",
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/@colors/colors": {
"version": "1.5.0",
"dev": true,
@ -444,6 +510,28 @@
"node": ">=0.1.90"
}
},
"node_modules/@emotion/is-prop-valid": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz",
"integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@emotion/memoize": "^0.9.0"
}
},
"node_modules/@emotion/memoize": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
"integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
"license": "MIT"
},
"node_modules/@emotion/unitless": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
"integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==",
"license": "MIT"
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.28.1",
"resolved": "http://134.199.243.61:4873/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
@ -1345,6 +1433,109 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@lilith/ui-accessibility": {
"version": "1.2.2",
"resolved": "http://134.199.243.61:4873/@lilith/ui-accessibility/-/ui-accessibility-1.2.2.tgz",
"integrity": "sha512-tb6W+ioZcRI11GzyEQOhuUWSMUulpISj9MtsuLasajKJ16gpjKq1DDW7w7Rby3OVx/7K2V1fIIRgirPK0G3xcQ==",
"license": "MIT",
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
}
},
"node_modules/@lilith/ui-design-tokens": {
"version": "1.2.1",
"resolved": "http://134.199.243.61:4873/@lilith/ui-design-tokens/-/ui-design-tokens-1.2.1.tgz",
"integrity": "sha512-eOVzcfEHXp/k3Kt5qR7oEw+sz8QRfFUf5ssnqfpo/St7W5mtdrgokBhXuhY8KWjtuvV9PnRFpHyDC2fGxGUI3Q=="
},
"node_modules/@lilith/ui-motion": {
"version": "2.2.0",
"resolved": "http://134.199.243.61:4873/@lilith/ui-motion/-/ui-motion-2.2.0.tgz",
"integrity": "sha512-ZoYUCDmyjYl/pG40aIN9+RZaQNXLV5P/3Y1P29MgxzmmVzNgtyY4vH0tzD92BIyxYFxxG1nwMIir1K4bBFfD6Q==",
"dependencies": {
"framer-motion": ">=11.18.2",
"motion-dom": "^11.18.1",
"motion-utils": "^11.18.1"
},
"peerDependencies": {
"framer-motion": ">=11.0.0",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
}
},
"node_modules/@lilith/ui-styled-components": {
"version": "6.3.9",
"resolved": "http://134.199.243.61:4873/@lilith/ui-styled-components/-/ui-styled-components-6.3.9.tgz",
"integrity": "sha512-vwgr0RwOZlPKW5KhP+A20yIMKW0nCdlYxy6yzdBDmqFEuf18y71mY4hwTJnVd5J4oWKf2nrMxIKkXFwj6qXLeA==",
"dependencies": {
"styled-components": "6.3.8"
},
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
}
},
"node_modules/@lilith/ui-styled-components/node_modules/postcss": {
"version": "8.4.49",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
"integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/@lilith/ui-styled-components/node_modules/styled-components": {
"version": "6.3.8",
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.3.8.tgz",
"integrity": "sha512-Kq/W41AKQloOqKM39zfaMdJ4BcYDw/N5CIq4/GTI0YjU6pKcZ1KKhk6b4du0a+6RA9pIfOP/eu94Ge7cu+PDCA==",
"license": "MIT",
"dependencies": {
"@emotion/is-prop-valid": "1.4.0",
"@emotion/unitless": "0.10.0",
"@types/stylis": "4.2.7",
"css-to-react-native": "3.2.0",
"csstype": "3.2.3",
"postcss": "8.4.49",
"shallowequal": "1.1.0",
"stylis": "4.3.6",
"tslib": "2.8.1"
},
"engines": {
"node": ">= 16"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/styled-components"
},
"peerDependencies": {
"react": ">= 16.8.0",
"react-dom": ">= 16.8.0"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
}
}
},
"node_modules/@lukeed/csprng": {
"version": "1.1.0",
"license": "MIT",
@ -2264,6 +2455,12 @@
"@types/react": "^18.0.0"
}
},
"node_modules/@types/stylis": {
"version": "4.2.7",
"resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.7.tgz",
"integrity": "sha512-VgDNokpBoKF+wrdvhAAfS55OMQpL6QRglwTwNC3kIgBrzZxA4WsFj+2eLfEA/uMUDzBcEhYmjSbwQakn/i3ajA==",
"license": "MIT"
},
"node_modules/@types/validator": {
"version": "13.15.10",
"license": "MIT"
@ -2898,6 +3095,15 @@
"node": ">=6"
}
},
"node_modules/camelize": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz",
"integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001799",
"dev": true,
@ -3242,9 +3448,28 @@
"node": ">= 8"
}
},
"node_modules/css-color-keywords": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
"integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==",
"license": "ISC",
"engines": {
"node": ">=4"
}
},
"node_modules/css-to-react-native": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz",
"integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==",
"license": "MIT",
"dependencies": {
"camelize": "^1.0.0",
"css-color-keywords": "^1.0.0",
"postcss-value-parser": "^4.0.2"
}
},
"node_modules/csstype": {
"version": "3.2.3",
"dev": true,
"license": "MIT"
},
"node_modules/dayjs": {
@ -3797,6 +4022,48 @@
"node": ">= 0.6"
}
},
"node_modules/framer-motion": {
"version": "12.42.0",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.42.0.tgz",
"integrity": "sha512-wp7EJnfWaaEScVygKv3e20udoRz+LbtxScsuTkakAxfXmt+ReC6WyPW2nINRAGvd+hG9odwcjBLyOTPjH5pBRA==",
"license": "MIT",
"dependencies": {
"motion-dom": "^12.42.0",
"motion-utils": "^12.39.0",
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/framer-motion/node_modules/motion-dom": {
"version": "12.42.0",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.42.0.tgz",
"integrity": "sha512-M63h4n8R+quJdNhBwuLlgxM+OLYa9+I/T2pzDRboB9fLXRdbou+Gw7Zury+SkpaCyACP1JHSjHgZ1EgTkBr30w==",
"license": "MIT",
"dependencies": {
"motion-utils": "^12.39.0"
}
},
"node_modules/framer-motion/node_modules/motion-utils": {
"version": "12.39.0",
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz",
"integrity": "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==",
"license": "MIT"
},
"node_modules/fresh": {
"version": "2.0.0",
"license": "MIT",
@ -4492,6 +4759,21 @@
"mkdirp": "bin/cmd.js"
}
},
"node_modules/motion-dom": {
"version": "11.18.1",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz",
"integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==",
"license": "MIT",
"dependencies": {
"motion-utils": "^11.18.1"
}
},
"node_modules/motion-utils": {
"version": "11.18.1",
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz",
"integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==",
"license": "MIT"
},
"node_modules/ms": {
"version": "2.1.3",
"license": "MIT"
@ -4557,7 +4839,6 @@
},
"node_modules/nanoid": {
"version": "3.3.15",
"dev": true,
"funding": [
{
"type": "github",
@ -4841,7 +5122,6 @@
},
"node_modules/picocolors": {
"version": "1.1.1",
"dev": true,
"license": "ISC"
},
"node_modules/picomatch": {
@ -4906,6 +5186,12 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/postcss-value-parser": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"license": "MIT"
},
"node_modules/postgres-array": {
"version": "2.0.0",
"license": "MIT",
@ -5003,6 +5289,7 @@
"node_modules/react-dom": {
"version": "18.3.1",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.2"
@ -5316,6 +5603,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/shallowequal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==",
"license": "MIT"
},
"node_modules/shebang-command": {
"version": "2.0.0",
"license": "MIT",
@ -5422,7 +5715,6 @@
},
"node_modules/source-map-js": {
"version": "1.2.1",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@ -5580,6 +5872,49 @@
"url": "https://github.com/sponsors/Borewit"
}
},
"node_modules/styled-components": {
"version": "6.4.3",
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.4.3.tgz",
"integrity": "sha512-wYXrhu+JmDjZ1Tv7O0OopGTfztbzun43Pjjhh2H+xc0h5A09dwpZ5FJbrifJDcL8g5TA9btpWOX2+iSRuJTExw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@emotion/is-prop-valid": "1.4.0",
"css-to-react-native": "3.2.0",
"csstype": "3.2.3",
"stylis": "4.3.6"
},
"engines": {
"node": ">= 16"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/styled-components"
},
"peerDependencies": {
"css-to-react-native": ">= 3.2.0",
"react": ">= 16.8.0",
"react-dom": ">= 16.8.0",
"react-native": ">= 0.68.0"
},
"peerDependenciesMeta": {
"css-to-react-native": {
"optional": true
},
"react-dom": {
"optional": true
},
"react-native": {
"optional": true
}
}
},
"node_modules/stylis": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz",
"integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==",
"license": "MIT"
},
"node_modules/supports-color": {
"version": "7.2.0",
"dev": true,
@ -6640,10 +6975,17 @@
}
},
"web": {
"name": "@prospector/web",
"version": "0.1.0",
"dependencies": {
"@cocotte/site-themes": "^0.1.1",
"@cocotte/ui-fab": "^2.3.10",
"@cocotte/ui-icons": "^1.2.3",
"@cocotte/ui-theme": "^1.5.2",
"@cocotte/ui-zname": "^1.2.5",
"react": "^18.3.1",
"react-dom": "^18.3.1"
"react-dom": "^18.3.1",
"styled-components": "^6.3.8"
},
"devDependencies": {
"@types/react": "^18.3.12",

View file

@ -9,8 +9,14 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@cocotte/site-themes": "^0.1.1",
"@cocotte/ui-fab": "^2.3.10",
"@cocotte/ui-icons": "^1.2.3",
"@cocotte/ui-theme": "^1.5.2",
"@cocotte/ui-zname": "^1.2.5",
"react": "^18.3.1",
"react-dom": "^18.3.1"
"react-dom": "^18.3.1",
"styled-components": "^6.3.8"
},
"devDependencies": {
"@types/react": "^18.3.12",

View file

@ -1,5 +1,7 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { ThemeProvider, GlobalScrollbarStyles } from '@cocotte/ui-theme';
import { luxeDarkTheme } from '@cocotte/site-themes';
import { App } from './App';
import './styles.css';
@ -8,9 +10,17 @@ if (!rootEl) {
throw new Error('Root element #root not found');
}
// Platform design system: the cocotte brand theme = the `luxe` adapter deep-merged
// with @cocotte/site-themes' luxe-dark customTheme (gold/pink/cream on near-black;
// lilith was the previous brand). cssVariables injects the semantic tokens onto
// :root so the remaining styles.css rules can reference them during the incremental
// migration to styled-components.
createRoot(rootEl).render(
<StrictMode>
<App />
<ThemeProvider themeName="luxe" customTheme={luxeDarkTheme} cssVariables>
<GlobalScrollbarStyles />
<App />
</ThemeProvider>
</StrictMode>,
);

View file

@ -46,6 +46,11 @@ export default defineConfig(({ mode }) => {
return {
plugins,
// Force a single styled-components (and React) instance across the app and the
// @cocotte/ui-* + @lilith/ui-styled-components design-system packages. Without
// this, ui-theme's ThemeProvider (which feeds the theme via its own copy) and
// app/styled components resolve different ThemeContexts → `theme` is undefined.
resolve: { dedupe: ['styled-components', 'react', 'react-dom'] },
server: { proxy: devProxy },
preview: { proxy: previewProxy },
};