@cronos-labs/ui 0.2.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 (2) hide show
  1. package/README.md +241 -0
  2. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,241 @@
1
+ # @cronos-labs/ui
2
+
3
+ Shared Header, Footer, SEO, locale and theme primitives for Cronos web
4
+ properties. Consumed by `cronos-landingpage-webui`, `cronos-bridge-webui`
5
+ and `cronos-launch-webui` so the three surfaces stay visually and
6
+ behaviourally identical without copying code between them.
7
+
8
+ The package is published publicly on npm; this repository is private.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install @cronos-labs/ui
14
+ ```
15
+
16
+ ### Peer dependencies
17
+
18
+ Required — the package will not work without them:
19
+
20
+ | Package | Range |
21
+ | ------------------- | --------- |
22
+ | `react` | `^19.1.0` |
23
+ | `react-dom` | `^19.1.0` |
24
+ | `styled-components` | `^6.1.15` |
25
+ | `react-router-dom` | `^7.1.5` |
26
+
27
+ Optional — only needed if you consume the shared ESLint config
28
+ (`@cronos-labs/ui/eslint-config`):
29
+
30
+ `@eslint/js`, `eslint`, `eslint-config-prettier`, `eslint-plugin-react-hooks`,
31
+ `eslint-plugin-react-refresh`, `globals`, `typescript-eslint`.
32
+
33
+ ## Entry points
34
+
35
+ The package ships compiled ESM plus type declarations. Four subpath
36
+ patterns are exported:
37
+
38
+ | Import | Contents |
39
+ | --------------------------------- | -------------------------------------------------------------- |
40
+ | `@cronos-labs/ui` | The barrel — every component, token and helper below |
41
+ | `@cronos-labs/ui/<path>` | Any single module, e.g. `locale/config`, `locale/localization` |
42
+ | `@cronos-labs/ui/eslint-config` | `createReactEslintConfig` |
43
+ | `@cronos-labs/ui/prettier-config` | The shared Prettier config, as a default export |
44
+
45
+ ### When to use a deep import instead of the barrel
46
+
47
+ **This is the most common way to break a build.** The barrel re-exports
48
+ components that depend on `react-router-dom` and `styled-components`. Any
49
+ context that evaluates modules with Node's own ESM loader rather than a
50
+ bundler — an Astro config file, a content-collection schema, a sitemap
51
+ script — cannot load it, and fails at import time with
52
+ `styled.div is not a function`.
53
+
54
+ Reach for the single module you actually need in those places:
55
+
56
+ ```js
57
+ // astro.config.mjs — evaluated by Node, not the bundler
58
+ import { localeConfig } from '@cronos-labs/ui/locale/config';
59
+ ```
60
+
61
+ ```ts
62
+ // src/content/config.ts — content collection schema
63
+ import { Locale } from '@cronos-labs/ui/locale/localization';
64
+ ```
65
+
66
+ Application code that goes through the bundler uses the barrel normally:
67
+
68
+ ```tsx
69
+ import { Header, Footer, tokens, useCurrentLocale } from '@cronos-labs/ui';
70
+ ```
71
+
72
+ ### Bundler configuration
73
+
74
+ Vite treats the package as external during SSR by default and loads it
75
+ with Node's native ESM loader, which does not apply the `__esModule`
76
+ CJS-interop convention `styled-components` relies on. Force it through
77
+ the same transform pipeline as `styled-components` itself:
78
+
79
+ ```js
80
+ // astro.config.mjs / vite.config.ts
81
+ ssr: {
82
+ noExternal: ['styled-components', '@cronos-labs/ui'],
83
+ }
84
+ ```
85
+
86
+ For Vitest, inline the package so component tests resolve it the same way:
87
+
88
+ ```ts
89
+ test: {
90
+ server: { deps: { inline: ['@cronos-labs/ui'] } },
91
+ }
92
+ ```
93
+
94
+ ## What's in the package
95
+
96
+ ### Components
97
+
98
+ | Export | Purpose |
99
+ | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
100
+ | `Header` | Site header: brand wordmark, nav with dropdowns, mobile drawer, locale selector, waitlist CTA |
101
+ | `Footer` | Site footer: link columns, legal links, cookie-preferences hook, disclaimer copy |
102
+ | `Seo` | Renders nothing; imperatively upserts `<title>`, meta and canonical tags. Creates tags that are absent rather than skipping them |
103
+ | `LocaleSelector` | Locale dropdown, shared by `Header` and `Footer` |
104
+ | `SectionAnchorNav` | In-page anchor nav that tracks the active section |
105
+ | `SearchField` | Labelled search input with optional clear button and status text |
106
+ | `PillButton`, `FilterPillRow` | Filter pill primitives |
107
+
108
+ Header and Footer are driven entirely by props — no data fetching, no
109
+ router coupling beyond `react-router-dom`. Their content strings arrive
110
+ through `HeaderContent` / `FooterContent` so each app supplies its own
111
+ localized copy.
112
+
113
+ Exported types: `HeaderPresentationVariant`, `HeaderPresentationState`,
114
+ `HeaderNavItem`, `HeaderNavChild`, `HeaderContent`, `HeaderWaitlistCta`,
115
+ `FooterLinkItem`, `FooterLinkGroup`, `FooterLegalLinkItem`,
116
+ `FooterContent`, `SeoMeta`, `RouteSeoMeta`, `SearchFieldProps`,
117
+ `SectionAnchorNavItem`.
118
+
119
+ ### Design tokens
120
+
121
+ `tokens` groups every shared value: `colors`, `sizes`, `mediaQueries`,
122
+ `borderRadius`, `fontWeight`, `typography`, `shadow`, `effects`, `motion`,
123
+ `layout`, `zIndex`. `desktopViewportQuery` is the `min-width: 1024px`
124
+ media query the components use to switch to their desktop layout.
125
+
126
+ ```ts
127
+ import { tokens, desktopViewportQuery } from '@cronos-labs/ui';
128
+ ```
129
+
130
+ ### Typography
131
+
132
+ Ready-made `styled-components` `css` blocks, so type scale stays
133
+ consistent across apps: `displayHeroTitleTypography`,
134
+ `pageHeroTitleTypography`, `articleHeroTitleTypography`,
135
+ `sectionTitleTypography`, `articleSectionTitleTypography`,
136
+ `cardTitleTypography`, `leadBodyTypography`, `articleBodyTypography`,
137
+ `compactBodyTypography`, `eyebrowTypography`, `metaTypography`.
138
+
139
+ ### Locale
140
+
141
+ Nine locales are supported: `en`, `en-in`, `en-ng`, `en-ph`, `id-id`,
142
+ `ko-kr`, `pt-br`, `es-mx`, `vi-vn`. The prefix strategy is `as-needed` —
143
+ the default locale has no path prefix, the rest do.
144
+
145
+ | Export | Purpose |
146
+ | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
147
+ | `Locale` | Enum of supported locale codes |
148
+ | `localeConfig` | Full config: default, fallback, supported list, per-locale labels and text direction |
149
+ | `isSupportedLocale` | Type guard for an unknown string |
150
+ | `resolveLocale` | Resolves a value to a locale, falling back when unrecognised |
151
+ | `stripLocalePrefix` | Removes the locale segment from a pathname |
152
+ | `buildLocalizedPath` | Prefixes a path for a locale, honouring `as-needed` |
153
+ | `useCurrentLocale` | Reads the active locale from the router |
154
+ | `useApplyLocaleDocumentAttributes` | Keeps `<html lang>` and `dir` in sync |
155
+ | `setStoredLocalePreference`, `getStoredLocalePreference`, `hasStoredLocalePreference`, `localePreferenceStorageKey` | Persisted locale preference (`cronos.locale`) |
156
+
157
+ Types: `AppLocale`, `LocaleConfig`, `LocaleDefinition`,
158
+ `LocalePrefixStrategy`, `TextDirection`, `LocalizedContentDictionary`,
159
+ `LocalizedPageMetadata`.
160
+
161
+ ### Utilities
162
+
163
+ | Export | Purpose |
164
+ | ---------------------------- | -------------------------------------------------------------------------------------------------------------------- |
165
+ | `navigationContent` | Shared nav structure, with the `NavigationContent` type |
166
+ | `normalizePathname` | Trailing-slash-insensitive pathname comparison used for nav active state |
167
+ | `interpolateMessageTemplate` | Fills `{placeholder}` tokens in localized strings |
168
+ | `scrollToSectionHeading` | Smooth-scrolls to a section's first `h1`/`h2` (or `[data-section-scroll-target]`), offset 128px for the fixed header |
169
+
170
+ ### Shared lint and format config
171
+
172
+ ```js
173
+ // eslint.config.mjs
174
+ import { createReactEslintConfig } from '@cronos-labs/ui/eslint-config';
175
+
176
+ export default createReactEslintConfig({
177
+ tsconfigRootDir: import.meta.dirname,
178
+ });
179
+ ```
180
+
181
+ `createReactEslintConfig` takes `tsconfigRootDir` (required) and optional
182
+ `ignores` and `allowDefaultProject`. It composes the recommended,
183
+ type-checked and stylistic `typescript-eslint` sets with the React Hooks
184
+ and React Refresh plugins, and disables rules Prettier owns.
185
+
186
+ ```json
187
+ // package.json
188
+ "prettier": "@cronos-labs/ui/prettier-config"
189
+ ```
190
+
191
+ ## Local development
192
+
193
+ ```bash
194
+ npm ci
195
+ npm run lint
196
+ npm run typecheck
197
+ npm run test
198
+ npm run build
199
+ npm run format:check
200
+ ```
201
+
202
+ `build` compiles `src/` to `dist/` with `tsc`, then rewrites every relative
203
+ specifier in `dist/**/*.js` and `dist/**/*.d.ts` to point at the concrete
204
+ emitted file. `tsc` emits extensionless specifiers under
205
+ `moduleResolution: "bundler"`, which plain Node ESM cannot resolve at
206
+ runtime. `dist/` is generated and not committed.
207
+
208
+ The repo formats itself with the same Prettier config it exports. A unit
209
+ test asserts `.prettierrc` and `src/config/prettierConfig.ts` stay in
210
+ sync, so the two copies cannot drift.
211
+
212
+ ### Structure rule
213
+
214
+ Styled React units use `ComponentName/index.tsx` for the component and
215
+ `ComponentName/styles.ts` for its styled definitions.
216
+
217
+ ## Releasing
218
+
219
+ Releases are cut by tag. Publishing happens in CI so the tarball is always
220
+ built on the same Node 22 runner consumers' pipelines use, rather than
221
+ whatever happens to be on a laptop.
222
+
223
+ 1. Land your change on `main` through a PR.
224
+ 2. Bump `version` in `package.json` (semver).
225
+ 3. Tag the merge commit and push:
226
+
227
+ ```bash
228
+ git tag ui-v<version>
229
+ git push origin ui-v<version>
230
+ ```
231
+
232
+ The release workflow refuses to publish if the tag and `package.json`
233
+ disagree on the version. `prepack` rebuilds `dist/` as part of packing, so
234
+ a stale build can never ship.
235
+
236
+ Consumers then bump their range and run `npm install`.
237
+
238
+ ## License
239
+
240
+ UNLICENSED. Published publicly on npm for installation convenience; not
241
+ open source.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cronos-labs/ui",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Shared Header, Footer, Seo, locale and theme primitives for Cronos web properties.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",