@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.
- package/README.md +241 -0
- 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.
|