@castui/cast-ui 0.1.0 → 0.3.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
@@ -33,6 +33,12 @@ Example:
33
33
 
34
34
  See [`design-tokens/DESIGN-TOKENS-SUMMARY.md`](design-tokens/DESIGN-TOKENS-SUMMARY.md) for the full token reference.
35
35
 
36
+ ## Examples
37
+
38
+ For live examples showing Cast UI components and theming in action, see the companion repo:
39
+
40
+ **[Connagh/cast-ui-examples](https://github.com/Connagh/cast-ui-examples)** (in progress)
41
+
36
42
  ## Project Structure
37
43
 
38
44
  ```
@@ -42,6 +48,9 @@ cast-ui/
42
48
  Button/
43
49
  Button.tsx RN Pressable + Text, theme-aware
44
50
  Button.stories.tsx Storybook stories
51
+ Card/
52
+ Card.tsx RN View + Text, theme-aware
53
+ Card.stories.tsx Storybook stories
45
54
  theme/
46
55
  types.ts CastTheme TypeScript interface
47
56
  ThemeProvider.tsx React Context provider + useTheme hook
@@ -68,6 +77,8 @@ cast-ui/
68
77
  preview-head.html Google Fonts <link> tags
69
78
  .github/workflows/
70
79
  chromatic.yml Visual regression testing on push
80
+ adoption.yml Zeroheight adoption tracking on push to main
81
+ publish.yml Publish to npm on push to main
71
82
  dist/ Build output (gitignored)
72
83
  tsconfig.json Development TypeScript config
73
84
  tsconfig.build.json Library build config (excludes stories)
@@ -113,6 +124,7 @@ Opens at `http://localhost:6006`. Use the paintbrush icon in the toolbar to swit
113
124
  | `npm run build-storybook` | Build static Storybook (auto-runs `build:tokens`) |
114
125
  | `npm run build` | Full library build: tokens + TypeScript compilation to `dist/` |
115
126
  | `npm publish` | Publish to npm (auto-runs `build` via `prepublishOnly`) |
127
+ | `npm run zh:track-package` | Register/update package info with Zeroheight (runs automatically via CI) |
116
128
 
117
129
  ## Themes
118
130
 
@@ -184,6 +196,7 @@ theme.semantic.paragraphSpacing.* body, editorial
184
196
  theme.semantic.paragraphIndent.* editorial
185
197
  theme.semantic.borderRadius.* small, medium, large
186
198
  theme.component.button.* All button tokens (padding, colours, variants, states)
199
+ theme.component.card.* All card tokens (padding, colours, typography, elevation)
187
200
  ```
188
201
 
189
202
  ## Components
@@ -207,6 +220,33 @@ import { Button } from '@castui/cast-ui';
207
220
  | `disabled` | `boolean` | `false` | Disabled state |
208
221
  | `backgroundColor` | `string` | - | Override background colour |
209
222
 
223
+ ### Card
224
+
225
+ React Native `View` + `Text`. Consumes tokens from `theme.component.card` and `theme.semantic`.
226
+
227
+ ```tsx
228
+ import { Card, Button } from '@castui/cast-ui';
229
+
230
+ <Card
231
+ title="Title"
232
+ subtitle="Subtitle"
233
+ body="Body"
234
+ actions={
235
+ <>
236
+ <Button label="Action 1" variant="filled" />
237
+ <Button label="Action 2" variant="outline" />
238
+ </>
239
+ }
240
+ />
241
+ ```
242
+
243
+ | Prop | Type | Default | Description |
244
+ |------|------|---------|-------------|
245
+ | `title` | `string` | required | Card heading text |
246
+ | `subtitle` | `string` | - | Optional subtitle below the title |
247
+ | `body` | `string` | - | Optional body text |
248
+ | `actions` | `React.ReactNode` | - | Optional actions row (e.g. Button components) |
249
+
210
250
  ## Font Handling
211
251
 
212
252
  Components reference font families from the theme tokens. Font **loading** is the consumer's responsibility.
@@ -224,14 +264,41 @@ const url = googleFontsUrl('luxury');
224
264
 
225
265
  ### React Native / Expo
226
266
 
227
- Load fonts with `expo-font` before rendering:
267
+ Load fonts with `expo-font` before rendering. On **Android**, each weight must
268
+ be registered under a distinct name (the Expo Google Fonts convention) because
269
+ Android cannot combine a generic `fontFamily` with a numeric `fontWeight` for
270
+ custom fonts.
228
271
 
229
272
  ```ts
230
273
  import { useFonts } from 'expo-font';
231
- import { THEME_FONT_FAMILIES } from '@castui/cast-ui';
232
- // THEME_FONT_FAMILIES.luxury = ['Playfair Display', 'Cormorant Garamond']
274
+ import {
275
+ Poppins_400Regular,
276
+ Poppins_500Medium,
277
+ Poppins_700Bold,
278
+ } from '@expo-google-fonts/poppins';
279
+
280
+ const [loaded] = useFonts({
281
+ Poppins: Poppins_400Regular, // weight 400 → bare name
282
+ Poppins_500Medium: Poppins_500Medium, // weight 500
283
+ Poppins_700Bold: Poppins_700Bold, // weight 700
284
+ });
233
285
  ```
234
286
 
287
+ The naming convention is:
288
+
289
+ | Weight | Registration Key |
290
+ |--------|-----------------|
291
+ | 400 | `"FontName"` |
292
+ | 500 | `"FontName_500Medium"` |
293
+ | 700 | `"FontName_700Bold"` |
294
+
295
+ Components use `resolveFont()` internally so the correct font name is selected
296
+ automatically on each platform:
297
+
298
+ - **iOS / Web** — `{ fontFamily, fontWeight }` passed through unchanged.
299
+ - **Android** — maps to the weight-specific registered name (e.g. `"Poppins_700Bold"`) and sets `fontWeight: 'normal'`.
300
+ - **system-ui** — omits `fontFamily` (platform default) on all platforms.
301
+
235
302
  The **White Label** theme uses `system-ui` (platform default) and requires no font loading.
236
303
 
237
304
  | Theme | Fonts to Load |
@@ -241,6 +308,11 @@ The **White Label** theme uses `system-ui` (platform default) and requires no fo
241
308
  | Corporate | Inter, Merriweather |
242
309
  | Luxury | Playfair Display, Cormorant Garamond |
243
310
 
311
+ > **Custom Themes:** The convention works for any font — register each weight
312
+ > under `FontName`, `FontName_500Medium`, and `FontName_700Bold` and
313
+ > `resolveFont()` will handle the rest. The `ANDROID_WEIGHT_SUFFIX` map is
314
+ > exported if you need to build registration keys programmatically.
315
+
244
316
  ## Consumer Installation
245
317
 
246
318
  ```bash
@@ -258,6 +330,8 @@ import {
258
330
  consumer,
259
331
  corporate,
260
332
  luxury,
333
+ resolveFont,
334
+ ANDROID_WEIGHT_SUFFIX,
261
335
  } from '@castui/cast-ui';
262
336
  ```
263
337
 
@@ -297,15 +371,111 @@ The build script (`src/tokens/build.ts`) reads Figma-exported JSON from `design-
297
371
  5. Export the component and its types from `src/index.ts`
298
372
  6. If the component needs new design tokens, add them to all four `*.tokens.json` files and update `src/theme/types.ts`
299
373
 
374
+ ## Adoption Tracking
375
+
376
+ Design system adoption is measured via [Zeroheight](https://zeroheight.com/). There are two sides to the setup: **this design system repo** and the **consumer app repos** that install it.
377
+
378
+ ### This repo (design system)
379
+
380
+ The only command relevant here is `track-package`. It registers `@castui/cast-ui` and its current version with Zeroheight so the dashboard knows what the latest release is.
381
+
382
+ This runs automatically on every push to `main` via the GitHub Actions workflow at `.github/workflows/adoption.yml`. You can also run it locally:
383
+
384
+ ```bash
385
+ npm run zh:track-package
386
+ ```
387
+
388
+ ### Consumer app repos
389
+
390
+ The adoption CLI is designed to be run **in the repositories that consume this design system**. These are the commands consumers (or their CI pipelines) should run:
391
+
392
+ | Command | What it measures |
393
+ |---------|-----------------|
394
+ | `npx @zeroheight/adoption-cli analyze --component-usage` | Which Cast UI components are imported and how their props are used |
395
+ | `npx @zeroheight/adoption-cli analyze --color-usage` | Hardcoded color values (`#hex`, `rgb()`, etc.) that should be replaced with theme tokens |
396
+ | `npx @zeroheight/adoption-cli monitor-repo` | Which version of `@castui/cast-ui` the consumer app is on |
397
+
398
+ **How color tracking works:** The `--color-usage` flag scans for *non-token* color values — raw hex, rgb, or hsl strings that a developer hardcoded instead of using `theme.semantic.color.*`. A high count means developers are bypassing the design system; a count trending to zero means strong token adoption. Running this in the design system repo itself correctly returns zero results because components only reference theme tokens, never hardcoded colours.
399
+
400
+ ### Consumer CI example
401
+
402
+ Consumer teams can automate adoption reporting by adding a workflow to their repos:
403
+
404
+ ```yaml
405
+ # .github/workflows/design-system-adoption.yml
406
+ name: "Design System Adoption"
407
+
408
+ on:
409
+ push:
410
+ branches: [main]
411
+
412
+ jobs:
413
+ adoption:
414
+ runs-on: ubuntu-latest
415
+ steps:
416
+ - uses: actions/checkout@v5
417
+ - uses: actions/setup-node@v6
418
+ with:
419
+ node-version: 20
420
+ - run: npm ci
421
+ - name: Report adoption to Zeroheight
422
+ env:
423
+ ZEROHEIGHT_CLIENT_ID: ${{ secrets.ZEROHEIGHT_CLIENT_ID }}
424
+ ZEROHEIGHT_ACCESS_TOKEN: ${{ secrets.ZEROHEIGHT_ACCESS_TOKEN }}
425
+ run: |
426
+ npx @zeroheight/adoption-cli analyze --component-usage --interactive false -r "${{ github.repository }}"
427
+ npx @zeroheight/adoption-cli analyze --color-usage --interactive false -r "${{ github.repository }}"
428
+ npx @zeroheight/adoption-cli monitor-repo --interactive false
429
+ ```
430
+
431
+ ### Required secrets
432
+
433
+ All Zeroheight CLI commands in CI require two repository secrets:
434
+
435
+ | Secret | Description |
436
+ |--------|-------------|
437
+ | `ZEROHEIGHT_CLIENT_ID` | OAuth client ID from your Zeroheight account |
438
+ | `ZEROHEIGHT_ACCESS_TOKEN` | Access token from your Zeroheight account |
439
+
440
+ Generate these at **Zeroheight > Account Settings > Access Tokens**.
441
+
300
442
  ## npm Publishing
301
443
 
302
- The package is configured for public npm publishing:
444
+ The publish workflow (`.github/workflows/publish.yml`) runs on every push to `main`. It **automatically checks** whether the `version` in `package.json` has changed compared to what's already on npm. If the version is the same, the workflow skips silently — no build, no publish. If the version is new, it builds and publishes.
445
+
446
+ This means merging a PR to `main` does **not** automatically publish to npm. Only PRs that include a version bump in `package.json` will trigger a release.
447
+
448
+ **To publish a new version:**
449
+
450
+ 1. Create a feature branch and make your changes
451
+ 2. Update the `version` field in `package.json` (e.g. `0.1.0` → `0.1.1`)
452
+ 3. Push the branch and open a PR to `main`
453
+ 4. Chromatic runs and creates status checks on the PR
454
+ 5. Review and accept any visual changes in the Chromatic UI
455
+ 6. Once Chromatic checks pass, merge the PR
456
+ 7. The publish workflow detects the new version, builds tokens + TypeScript, and publishes to npm
457
+
458
+ **If you don't bump the version:** The PR merges normally, Chromatic still runs, but the publish step is skipped. Your code is on `main` but no new npm version is created. This is useful for documentation changes, CI updates, or batching multiple changes before a release.
459
+
460
+ **If Chromatic changes are rejected:** Fix the code on your feature branch, push again. Chromatic re-runs with the new changes. Repeat until the visuals are right, then accept and merge.
461
+
462
+ **Version numbering guide:**
463
+
464
+ | Change type | Bump | Example |
465
+ |-------------|------|---------|
466
+ | Bug fix or minor tweak | Patch | `0.1.0` → `0.1.1` |
467
+ | New component or feature | Minor | `0.1.0` → `0.2.0` |
468
+ | Breaking API change | Major | `0.1.0` → `1.0.0` |
469
+
470
+ **Package details:**
303
471
 
304
472
  - **Entry point:** `dist/index.js` (CJS) with `dist/index.d.ts` type declarations
305
473
  - **Included files:** `dist/`, `README.md`, `LICENSE`
306
474
  - **Peer dependencies:** `react` (>=18), `react-native` (>=0.72)
307
475
  - **License:** MIT
308
476
 
477
+ You can still publish manually if needed:
478
+
309
479
  ```bash
310
480
  npm login
311
481
  npm publish
@@ -326,8 +496,34 @@ When adding new top-level files or directories, you must add a `!path` entry to
326
496
  | Workflow | Trigger | Purpose |
327
497
  |----------|---------|---------|
328
498
  | Chromatic (`.github/workflows/chromatic.yml`) | Every push | Visual regression testing via Storybook snapshots |
499
+ | Adoption Tracking (`.github/workflows/adoption.yml`) | Push to `main` | Registers package version with Zeroheight |
500
+ | Publish to npm (`.github/workflows/publish.yml`) | Push to `main` | Builds and publishes package to npm |
501
+
502
+ **Required secrets:**
503
+
504
+ | Secret | Used by |
505
+ |--------|---------|
506
+ | `CHROMATIC_PROJECT_TOKEN` | Chromatic workflow |
507
+ | `ZEROHEIGHT_CLIENT_ID` | Adoption Tracking workflow |
508
+ | `ZEROHEIGHT_ACCESS_TOKEN` | Adoption Tracking workflow |
509
+
510
+ The Publish workflow authenticates to npm via **OpenID Connect (OIDC)** using npm's Trusted Publishers feature — no `NPM_TOKEN` secret is needed. The trust relationship is configured at [npmjs.com](https://www.npmjs.com/) under **Package Settings → Trusted Publishers**. The `--provenance` flag attaches a verified build attestation to each published version.
511
+
512
+ ### Branch Protection
513
+
514
+ Branch protection on `main` ensures that **no code is merged without passing Chromatic visual checks**. This is what prevents unreviewed visual changes from being published to npm.
515
+
516
+ **To set up (one-time, on GitHub):**
517
+
518
+ 1. Go to **Settings → Branches** in your repository
519
+ 2. Click **Add branch protection rule**
520
+ 3. Set **Branch name pattern** to `main`
521
+ 4. Enable **Require a pull request before merging**
522
+ 5. Enable **Require status checks to pass before merging**
523
+ 6. Search for and add **Run Chromatic** (GitHub Actions) and **UI Tests** (any source) as required checks
524
+ 7. Click **Save changes**
329
525
 
330
- **Required secrets:** `CHROMATIC_PROJECT_TOKEN` (repository secret).
526
+ Once enabled, you can no longer push directly to `main`. All changes go through feature branches and PRs, with Chromatic as a mandatory gate.
331
527
 
332
528
  ## Dependencies
333
529
 
@@ -1 +1 @@
1
- {"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../../src/components/Button/Button.tsx"],"names":[],"mappings":"AACA,OAAO,EAML,KAAK,cAAc,EACpB,MAAM,cAAc,CAAC;AAOtB,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;AAE1D,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC;IAChE,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,iCAAiC;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAMD;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,EACrB,KAAK,EACL,OAAkB,EAClB,eAAe,EACf,QAAgB,EAChB,GAAG,cAAc,EAClB,EAAE,WAAW,2CA0Eb"}
1
+ {"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../../src/components/Button/Button.tsx"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,cAAc,EACpB,MAAM,cAAc,CAAC;AAOtB,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;AAE1D,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC;IAChE,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,iCAAiC;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAMD;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,EACrB,KAAK,EACL,OAAkB,EAClB,eAAe,EACf,QAAgB,EAChB,GAAG,cAAc,EAClB,EAAE,WAAW,2CAuEb"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
3
  import { Pressable, Text, } from 'react-native';
4
- import { useTheme } from '../../theme';
4
+ import { useTheme, resolveFont } from '../../theme';
5
5
  // ---------------------------------------------------------------------------
6
6
  // Component
7
7
  // ---------------------------------------------------------------------------
@@ -58,13 +58,11 @@ export function Button({ label, variant = 'filled', backgroundColor, disabled =
58
58
  }),
59
59
  opacity: disabled ? 0.6 : 1,
60
60
  };
61
- const fontFamily = bt.fontFamily === 'system-ui' ? undefined : bt.fontFamily;
62
61
  const textStyle = {
63
62
  color: resolveContent(),
64
63
  fontSize: bt.textSize,
65
- fontWeight: String(bt.fontWeight),
66
64
  lineHeight: bt.textSize * bt.lineHeight,
67
- ...(fontFamily ? { fontFamily } : {}),
65
+ ...resolveFont(bt.fontFamily, bt.fontWeight),
68
66
  };
69
67
  return (_jsx(Pressable, { ...pressableProps, disabled: disabled, style: containerStyle, ...{
70
68
  onHoverIn: () => setHovered(true),
@@ -1 +1 @@
1
- {"version":3,"file":"Button.js","sourceRoot":"","sources":["../../../src/components/Button/Button.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Button.js","sourceRoot":"","sources":["../../../src/components/Button/Button.tsx"],"names":[],"mappings":"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"}
@@ -2,17 +2,19 @@ import React from 'react';
2
2
  import { type ViewProps } from 'react-native';
3
3
  export interface CardProps extends Omit<ViewProps, 'style'> {
4
4
  /** Card title text. */
5
- heading?: string;
6
- /** Card body text. */
5
+ title: string;
6
+ /** Optional subtitle displayed below the title. */
7
+ subtitle?: string;
8
+ /** Optional body text. */
7
9
  body?: string;
8
- /** Override card content with arbitrary children. */
9
- children?: React.ReactNode;
10
+ /** Optional actions row (e.g. Button components). */
11
+ actions?: React.ReactNode;
10
12
  }
11
13
  /**
12
- * Themed container for grouping related content.
14
+ * Card container for grouping related content.
13
15
  *
14
- * Renders as a React Native `View` – works identically on iOS, Android,
15
- * and Web (via React Native Web).
16
+ * Renders as a React Native `View` with `Text` children – works identically
17
+ * on iOS, Android, and Web (via React Native Web).
16
18
  */
17
- export declare function Card({ heading, body, children, ...viewProps }: CardProps): import("react/jsx-runtime").JSX.Element;
19
+ export declare function Card({ title, subtitle, body, actions, ...viewProps }: CardProps): import("react/jsx-runtime").JSX.Element;
18
20
  //# sourceMappingURL=Card.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Card.d.ts","sourceRoot":"","sources":["../../../src/components/Card/Card.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAOtB,MAAM,WAAW,SAAU,SAAQ,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC;IACzD,uBAAuB;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sBAAsB;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAMD;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,SAAS,EAAE,EAAE,SAAS,2CAqDxE"}
1
+ {"version":3,"file":"Card.d.ts","sourceRoot":"","sources":["../../../src/components/Card/Card.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAOtB,MAAM,WAAW,SAAU,SAAQ,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC;IACzD,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0BAA0B;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAMD;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,EACnB,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,OAAO,EACP,GAAG,SAAS,EACb,EAAE,SAAS,2CA0DX"}
@@ -1,53 +1,58 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { View, Text, } from 'react-native';
3
- import { useTheme } from '../../theme';
3
+ import { useTheme, resolveFont } from '../../theme';
4
4
  // ---------------------------------------------------------------------------
5
5
  // Component
6
6
  // ---------------------------------------------------------------------------
7
7
  /**
8
- * Themed container for grouping related content.
8
+ * Card container for grouping related content.
9
9
  *
10
- * Renders as a React Native `View` – works identically on iOS, Android,
11
- * and Web (via React Native Web).
10
+ * Renders as a React Native `View` with `Text` children – works identically
11
+ * on iOS, Android, and Web (via React Native Web).
12
12
  */
13
- export function Card({ heading, body, children, ...viewProps }) {
13
+ export function Card({ title, subtitle, body, actions, ...viewProps }) {
14
14
  const theme = useTheme();
15
15
  const ct = theme.component.card;
16
- // --- container styles ------------------------------------------------------
16
+ const sem = theme.semantic;
17
+ // --- build styles ----------------------------------------------------------
17
18
  const containerStyle = {
18
19
  padding: ct.padding,
19
20
  gap: ct.gap,
20
- backgroundColor: ct.background,
21
21
  borderRadius: ct.cornerRadius,
22
+ backgroundColor: ct.background,
22
23
  borderWidth: ct.strokeWidth,
23
24
  borderColor: ct.stroke,
24
- // Elevation / shadow
25
- ...(ct.elevation > 0 && {
26
- // iOS shadows
27
- shadowColor: '#000',
28
- shadowOffset: { width: 0, height: ct.elevation },
29
- shadowOpacity: 0.08 * ct.elevation,
30
- shadowRadius: ct.elevation * 2,
31
- // Android elevation
32
- elevation: ct.elevation,
33
- }),
25
+ elevation: ct.elevation,
26
+ // Web shadow for elevation (React Native Web doesn't map elevation to CSS)
27
+ ...(ct.elevation > 0
28
+ ? { boxShadow: '0 1px 3px rgba(0,0,0,0.12)' }
29
+ : {}),
34
30
  };
35
- // --- typography styles -----------------------------------------------------
36
- const headingFontFamily = ct.headingFontFamily === 'system-ui' ? undefined : ct.headingFontFamily;
37
- const bodyFontFamily = ct.bodyFontFamily === 'system-ui' ? undefined : ct.bodyFontFamily;
38
- const headingStyle = {
31
+ const titleStyle = {
39
32
  fontSize: ct.headingSize,
40
- fontWeight: String(ct.headingWeight),
41
- color: theme.semantic.color.onSurface,
42
- ...(headingFontFamily ? { fontFamily: headingFontFamily } : {}),
33
+ lineHeight: ct.headingSize * sem.lineHeight.body,
34
+ letterSpacing: sem.letterSpacing.heading,
35
+ color: sem.color.onSurface,
36
+ ...resolveFont(ct.headingFontFamily, ct.headingWeight),
37
+ };
38
+ const subtitleStyle = {
39
+ fontSize: sem.fontSize.small,
40
+ lineHeight: sem.fontSize.small * sem.lineHeight.body,
41
+ letterSpacing: sem.letterSpacing.body,
42
+ color: sem.color.onSurfaceMuted,
43
+ ...resolveFont(sem.fontFamily.interface, sem.fontWeight.body),
43
44
  };
44
45
  const bodyStyle = {
45
46
  fontSize: ct.bodySize,
46
- fontWeight: String(ct.bodyWeight),
47
- color: theme.semantic.color.onSurfaceMuted,
48
- lineHeight: ct.bodySize * theme.semantic.lineHeight.body,
49
- ...(bodyFontFamily ? { fontFamily: bodyFontFamily } : {}),
47
+ lineHeight: ct.bodySize * sem.lineHeight.body,
48
+ letterSpacing: sem.letterSpacing.body,
49
+ color: sem.color.onSurfaceMuted,
50
+ ...resolveFont(ct.bodyFontFamily, ct.bodyWeight),
51
+ };
52
+ const actionsStyle = {
53
+ flexDirection: 'row',
54
+ gap: ct.gap,
50
55
  };
51
- return (_jsxs(View, { ...viewProps, style: containerStyle, children: [heading != null && _jsx(Text, { style: headingStyle, children: heading }), body != null && _jsx(Text, { style: bodyStyle, children: body }), children] }));
56
+ return (_jsxs(View, { ...viewProps, style: containerStyle, accessibilityRole: "summary", children: [_jsx(Text, { style: titleStyle, children: title }), subtitle ? _jsx(Text, { style: subtitleStyle, children: subtitle }) : null, body ? _jsx(Text, { style: bodyStyle, children: body }) : null, actions ? _jsx(View, { style: actionsStyle, children: actions }) : null] }));
52
57
  }
53
58
  //# sourceMappingURL=Card.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Card.js","sourceRoot":"","sources":["../../../src/components/Card/Card.tsx"],"names":[],"mappings":";AACA,OAAO,EACL,IAAI,EACJ,IAAI,GAIL,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAevC,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E;;;;;GAKG;AACH,MAAM,UAAU,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,SAAS,EAAa;IACvE,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;IACzB,MAAM,EAAE,GAAG,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC;IAEhC,8EAA8E;IAC9E,MAAM,cAAc,GAAc;QAChC,OAAO,EAAE,EAAE,CAAC,OAAO;QACnB,GAAG,EAAE,EAAE,CAAC,GAAG;QACX,eAAe,EAAE,EAAE,CAAC,UAAU;QAC9B,YAAY,EAAE,EAAE,CAAC,YAAY;QAC7B,WAAW,EAAE,EAAE,CAAC,WAAW;QAC3B,WAAW,EAAE,EAAE,CAAC,MAAM;QACtB,qBAAqB;QACrB,GAAG,CAAC,EAAE,CAAC,SAAS,GAAG,CAAC,IAAI;YACtB,cAAc;YACd,WAAW,EAAE,MAAM;YACnB,YAAY,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,SAAS,EAAE;YAChD,aAAa,EAAE,IAAI,GAAG,EAAE,CAAC,SAAS;YAClC,YAAY,EAAE,EAAE,CAAC,SAAS,GAAG,CAAC;YAC9B,oBAAoB;YACpB,SAAS,EAAE,EAAE,CAAC,SAAS;SACxB,CAAC;KACH,CAAC;IAEF,8EAA8E;IAC9E,MAAM,iBAAiB,GACrB,EAAE,CAAC,iBAAiB,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,iBAAiB,CAAC;IAE1E,MAAM,cAAc,GAClB,EAAE,CAAC,cAAc,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,cAAc,CAAC;IAEpE,MAAM,YAAY,GAAc;QAC9B,QAAQ,EAAE,EAAE,CAAC,WAAW;QACxB,UAAU,EAAE,MAAM,CAAC,EAAE,CAAC,aAAa,CAA4B;QAC/D,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS;QACrC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChE,CAAC;IAEF,MAAM,SAAS,GAAc;QAC3B,QAAQ,EAAE,EAAE,CAAC,QAAQ;QACrB,UAAU,EAAE,MAAM,CAAC,EAAE,CAAC,UAAU,CAA4B;QAC5D,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc;QAC1C,UAAU,EAAE,EAAE,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI;QACxD,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1D,CAAC;IAEF,OAAO,CACL,MAAC,IAAI,OAAK,SAAS,EAAE,KAAK,EAAE,cAAc,aACvC,OAAO,IAAI,IAAI,IAAI,KAAC,IAAI,IAAC,KAAK,EAAE,YAAY,YAAG,OAAO,GAAQ,EAC9D,IAAI,IAAI,IAAI,IAAI,KAAC,IAAI,IAAC,KAAK,EAAE,SAAS,YAAG,IAAI,GAAQ,EACrD,QAAQ,IACJ,CACR,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"Card.js","sourceRoot":"","sources":["../../../src/components/Card/Card.tsx"],"names":[],"mappings":";AACA,OAAO,EACL,IAAI,EACJ,IAAI,GAIL,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAiBpD,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E;;;;;GAKG;AACH,MAAM,UAAU,IAAI,CAAC,EACnB,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,OAAO,EACP,GAAG,SAAS,EACF;IACV,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;IACzB,MAAM,EAAE,GAAG,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC;IAChC,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC;IAE3B,8EAA8E;IAE9E,MAAM,cAAc,GAAc;QAChC,OAAO,EAAE,EAAE,CAAC,OAAO;QACnB,GAAG,EAAE,EAAE,CAAC,GAAG;QACX,YAAY,EAAE,EAAE,CAAC,YAAY;QAC7B,eAAe,EAAE,EAAE,CAAC,UAAU;QAC9B,WAAW,EAAE,EAAE,CAAC,WAAW;QAC3B,WAAW,EAAE,EAAE,CAAC,MAAM;QACtB,SAAS,EAAE,EAAE,CAAC,SAAS;QACvB,2EAA2E;QAC3E,GAAG,CAAC,EAAE,CAAC,SAAS,GAAG,CAAC;YAClB,CAAC,CAAE,EAAE,SAAS,EAAE,4BAA4B,EAA8B;YAC1E,CAAC,CAAC,EAAE,CAAC;KACR,CAAC;IAEF,MAAM,UAAU,GAAc;QAC5B,QAAQ,EAAE,EAAE,CAAC,WAAW;QACxB,UAAU,EAAE,EAAE,CAAC,WAAW,GAAG,GAAG,CAAC,UAAU,CAAC,IAAI;QAChD,aAAa,EAAE,GAAG,CAAC,aAAa,CAAC,OAAO;QACxC,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,SAAS;QAC1B,GAAG,WAAW,CAAC,EAAE,CAAC,iBAAiB,EAAE,EAAE,CAAC,aAAa,CAAC;KACvD,CAAC;IAEF,MAAM,aAAa,GAAc;QAC/B,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,KAAK;QAC5B,UAAU,EAAE,GAAG,CAAC,QAAQ,CAAC,KAAK,GAAG,GAAG,CAAC,UAAU,CAAC,IAAI;QACpD,aAAa,EAAE,GAAG,CAAC,aAAa,CAAC,IAAI;QACrC,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,cAAc;QAC/B,GAAG,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,EAAE,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC;KAC9D,CAAC;IAEF,MAAM,SAAS,GAAc;QAC3B,QAAQ,EAAE,EAAE,CAAC,QAAQ;QACrB,UAAU,EAAE,EAAE,CAAC,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC,IAAI;QAC7C,aAAa,EAAE,GAAG,CAAC,aAAa,CAAC,IAAI;QACrC,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,cAAc;QAC/B,GAAG,WAAW,CAAC,EAAE,CAAC,cAAc,EAAE,EAAE,CAAC,UAAU,CAAC;KACjD,CAAC;IAEF,MAAM,YAAY,GAAc;QAC9B,aAAa,EAAE,KAAK;QACpB,GAAG,EAAE,EAAE,CAAC,GAAG;KACZ,CAAC;IAEF,OAAO,CACL,MAAC,IAAI,OAAK,SAAS,EAAE,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAC,SAAS,aACrE,KAAC,IAAI,IAAC,KAAK,EAAE,UAAU,YAAG,KAAK,GAAQ,EACtC,QAAQ,CAAC,CAAC,CAAC,KAAC,IAAI,IAAC,KAAK,EAAE,aAAa,YAAG,QAAQ,GAAQ,CAAC,CAAC,CAAC,IAAI,EAC/D,IAAI,CAAC,CAAC,CAAC,KAAC,IAAI,IAAC,KAAK,EAAE,SAAS,YAAG,IAAI,GAAQ,CAAC,CAAC,CAAC,IAAI,EACnD,OAAO,CAAC,CAAC,CAAC,KAAC,IAAI,IAAC,KAAK,EAAE,YAAY,YAAG,OAAO,GAAQ,CAAC,CAAC,CAAC,IAAI,IACxD,CACR,CAAC;AACJ,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  export { CastThemeProvider, useTheme } from './theme';
2
2
  export type { CastThemeProviderProps } from './theme';
3
- export type { CastTheme, ThemeName, SemanticTokens, ComponentTokens, ButtonTokens, } from './theme';
4
- export { THEME_FONT_FAMILIES, googleFontsUrl } from './theme';
3
+ export type { CastTheme, ThemeName, SemanticTokens, ComponentTokens, ButtonTokens, CardTokens, } from './theme';
4
+ export { THEME_FONT_FAMILIES, googleFontsUrl, resolveFont, ANDROID_WEIGHT_SUFFIX } from './theme';
5
5
  export { whiteLabel, consumer, corporate, luxury, } from './tokens/generated';
6
6
  export { Button } from './components/Button/Button';
7
7
  export type { ButtonProps, ButtonVariant } from './components/Button/Button';
8
+ export { Card } from './components/Card/Card';
9
+ export type { CardProps } from './components/Card/Card';
8
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACtD,YAAY,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AACtD,YAAY,EACV,SAAS,EACT,SAAS,EACT,cAAc,EACd,eAAe,EACf,YAAY,GACb,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9D,OAAO,EACL,UAAU,EACV,QAAQ,EACR,SAAS,EACT,MAAM,GACP,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACtD,YAAY,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AACtD,YAAY,EACV,SAAS,EACT,SAAS,EACT,cAAc,EACd,eAAe,EACf,YAAY,EACZ,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAGlG,OAAO,EACL,UAAU,EACV,QAAQ,EACR,SAAS,EACT,MAAM,GACP,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAE7E,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAC9C,YAAY,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC"}
package/dist/index.js CHANGED
@@ -3,9 +3,10 @@
3
3
  // ---------------------------------------------------------------------------
4
4
  // Theme system
5
5
  export { CastThemeProvider, useTheme } from './theme';
6
- export { THEME_FONT_FAMILIES, googleFontsUrl } from './theme';
6
+ export { THEME_FONT_FAMILIES, googleFontsUrl, resolveFont, ANDROID_WEIGHT_SUFFIX } from './theme';
7
7
  // Theme objects
8
8
  export { whiteLabel, consumer, corporate, luxury, } from './tokens/generated';
9
9
  // Components
10
10
  export { Button } from './components/Button/Button';
11
+ export { Card } from './components/Card/Card';
11
12
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,kCAAkC;AAClC,8EAA8E;AAE9E,eAAe;AACf,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAStD,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9D,gBAAgB;AAChB,OAAO,EACL,UAAU,EACV,QAAQ,EACR,SAAS,EACT,MAAM,GACP,MAAM,oBAAoB,CAAC;AAE5B,aAAa;AACb,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,kCAAkC;AAClC,8EAA8E;AAE9E,eAAe;AACf,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAUtD,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAElG,gBAAgB;AAChB,OAAO,EACL,UAAU,EACV,QAAQ,EACR,SAAS,EACT,MAAM,GACP,MAAM,oBAAoB,CAAC;AAE5B,aAAa;AACb,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAGpD,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC"}
@@ -17,7 +17,23 @@
17
17
  *
18
18
  * **Web / Storybook** – fonts are loaded via `<link>` tags in
19
19
  * `.storybook/preview-head.html`. No runtime font loading is needed.
20
+ *
21
+ * ### Android font registration convention
22
+ *
23
+ * Android cannot combine a generic `fontFamily` with a numeric `fontWeight`
24
+ * for custom fonts — it silently falls back to the system font. Instead,
25
+ * each weight must be registered under a distinct name that matches the Expo
26
+ * Google Fonts convention:
27
+ *
28
+ * | Weight | Registration key |
29
+ * |--------|------------------------------|
30
+ * | 400 | `"FontName"` |
31
+ * | 500 | `"FontName_500Medium"` |
32
+ * | 700 | `"FontName_700Bold"` |
33
+ *
34
+ * Use {@link resolveFont} in component styles to transparently handle this.
20
35
  */
36
+ import { type TextStyle } from 'react-native';
21
37
  import type { ThemeName } from './types';
22
38
  /**
23
39
  * Map of Google Fonts family names used by each theme.
@@ -30,4 +46,22 @@ export declare const THEME_FONT_FAMILIES: Record<ThemeName, string[]>;
30
46
  * Useful if you need to programmatically inject font links on the web.
31
47
  */
32
48
  export declare function googleFontsUrl(themeName: ThemeName): string | null;
49
+ /**
50
+ * Suffix appended to a font family name on Android to select a specific
51
+ * weight. Matches the Expo Google Fonts registration convention.
52
+ *
53
+ * Weight 400 maps to the bare family name (empty suffix).
54
+ */
55
+ export declare const ANDROID_WEIGHT_SUFFIX: Record<number, string>;
56
+ /**
57
+ * Return the correct `fontFamily` / `fontWeight` style props for the
58
+ * current platform.
59
+ *
60
+ * - **iOS / Web** — returns `{ fontFamily, fontWeight }` unchanged.
61
+ * - **Android** — maps to a weight-specific registered font name
62
+ * (e.g. `"Poppins_700Bold"`) and resets `fontWeight` to `'normal'`.
63
+ * - **system-ui** — omits `fontFamily` entirely (platform default)
64
+ * and passes `fontWeight` through on all platforms.
65
+ */
66
+ export declare function resolveFont(fontFamily: string, fontWeight: number): Pick<TextStyle, 'fontFamily' | 'fontWeight'>;
33
67
  //# sourceMappingURL=fonts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../../src/theme/fonts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,CAK3D,CAAC;AAEF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CASlE"}
1
+ {"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../../src/theme/fonts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,CAK3D,CAAC;AAEF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CASlE;AAMD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAIxD,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,IAAI,CAAC,SAAS,EAAE,YAAY,GAAG,YAAY,CAAC,CAmB9C"}
@@ -17,7 +17,23 @@
17
17
  *
18
18
  * **Web / Storybook** – fonts are loaded via `<link>` tags in
19
19
  * `.storybook/preview-head.html`. No runtime font loading is needed.
20
+ *
21
+ * ### Android font registration convention
22
+ *
23
+ * Android cannot combine a generic `fontFamily` with a numeric `fontWeight`
24
+ * for custom fonts — it silently falls back to the system font. Instead,
25
+ * each weight must be registered under a distinct name that matches the Expo
26
+ * Google Fonts convention:
27
+ *
28
+ * | Weight | Registration key |
29
+ * |--------|------------------------------|
30
+ * | 400 | `"FontName"` |
31
+ * | 500 | `"FontName_500Medium"` |
32
+ * | 700 | `"FontName_700Bold"` |
33
+ *
34
+ * Use {@link resolveFont} in component styles to transparently handle this.
20
35
  */
36
+ import { Platform } from 'react-native';
21
37
  /**
22
38
  * Map of Google Fonts family names used by each theme.
23
39
  * The `system-ui` entry means "use the platform default" and requires
@@ -42,4 +58,45 @@ export function googleFontsUrl(themeName) {
42
58
  .join('&');
43
59
  return `https://fonts.googleapis.com/css2?${params}&display=swap`;
44
60
  }
61
+ // ---------------------------------------------------------------------------
62
+ // Android font-weight resolution
63
+ // ---------------------------------------------------------------------------
64
+ /**
65
+ * Suffix appended to a font family name on Android to select a specific
66
+ * weight. Matches the Expo Google Fonts registration convention.
67
+ *
68
+ * Weight 400 maps to the bare family name (empty suffix).
69
+ */
70
+ export const ANDROID_WEIGHT_SUFFIX = {
71
+ 400: '',
72
+ 500: '_500Medium',
73
+ 700: '_700Bold',
74
+ };
75
+ /**
76
+ * Return the correct `fontFamily` / `fontWeight` style props for the
77
+ * current platform.
78
+ *
79
+ * - **iOS / Web** — returns `{ fontFamily, fontWeight }` unchanged.
80
+ * - **Android** — maps to a weight-specific registered font name
81
+ * (e.g. `"Poppins_700Bold"`) and resets `fontWeight` to `'normal'`.
82
+ * - **system-ui** — omits `fontFamily` entirely (platform default)
83
+ * and passes `fontWeight` through on all platforms.
84
+ */
85
+ export function resolveFont(fontFamily, fontWeight) {
86
+ const weight = String(fontWeight);
87
+ // system-ui → platform default; just pass through fontWeight
88
+ if (fontFamily === 'system-ui') {
89
+ return { fontWeight: weight };
90
+ }
91
+ // Android needs a weight-specific registered name
92
+ if (Platform.OS === 'android') {
93
+ const suffix = ANDROID_WEIGHT_SUFFIX[fontWeight] ?? '';
94
+ return {
95
+ fontFamily: `${fontFamily}${suffix}`,
96
+ fontWeight: 'normal',
97
+ };
98
+ }
99
+ // iOS / Web – pass through unchanged
100
+ return { fontFamily, fontWeight: weight };
101
+ }
45
102
  //# sourceMappingURL=fonts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fonts.js","sourceRoot":"","sources":["../../src/theme/fonts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAgC;IAC9D,aAAa,EAAE,EAAE,EAA8C,iBAAiB;IAChF,QAAQ,EAAE,CAAC,SAAS,CAAC,EAA0C,iBAAiB;IAChF,SAAS,EAAE,CAAC,OAAO,EAAE,cAAc,CAAC,EAA2B,wBAAwB;IACvF,MAAM,EAAE,CAAC,kBAAkB,EAAE,oBAAoB,CAAC,EAAa,kBAAkB;CAClF,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,SAAoB;IACjD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;IAChD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvC,MAAM,MAAM,GAAG,QAAQ;SACpB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,kBAAkB,CAAC,CAAC,CAAC,mBAAmB,CAAC;SAC9D,IAAI,CAAC,GAAG,CAAC,CAAC;IAEb,OAAO,qCAAqC,MAAM,eAAe,CAAC;AACpE,CAAC"}
1
+ {"version":3,"file":"fonts.js","sourceRoot":"","sources":["../../src/theme/fonts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAAE,QAAQ,EAAkB,MAAM,cAAc,CAAC;AAGxD;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAgC;IAC9D,aAAa,EAAE,EAAE,EAA8C,iBAAiB;IAChF,QAAQ,EAAE,CAAC,SAAS,CAAC,EAA0C,iBAAiB;IAChF,SAAS,EAAE,CAAC,OAAO,EAAE,cAAc,CAAC,EAA2B,wBAAwB;IACvF,MAAM,EAAE,CAAC,kBAAkB,EAAE,oBAAoB,CAAC,EAAa,kBAAkB;CAClF,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,SAAoB;IACjD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;IAChD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvC,MAAM,MAAM,GAAG,QAAQ;SACpB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,kBAAkB,CAAC,CAAC,CAAC,mBAAmB,CAAC;SAC9D,IAAI,CAAC,GAAG,CAAC,CAAC;IAEb,OAAO,qCAAqC,MAAM,eAAe,CAAC;AACpE,CAAC;AAED,8EAA8E;AAC9E,iCAAiC;AACjC,8EAA8E;AAE9E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAA2B;IAC3D,GAAG,EAAE,EAAE;IACP,GAAG,EAAE,YAAY;IACjB,GAAG,EAAE,UAAU;CAChB,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CACzB,UAAkB,EAClB,UAAkB;IAElB,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAA4B,CAAC;IAE7D,6DAA6D;IAC7D,IAAI,UAAU,KAAK,WAAW,EAAE,CAAC;QAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;IAChC,CAAC;IAED,kDAAkD;IAClD,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,qBAAqB,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QACvD,OAAO;YACL,UAAU,EAAE,GAAG,UAAU,GAAG,MAAM,EAAE;YACpC,UAAU,EAAE,QAAQ;SACrB,CAAC;IACJ,CAAC;IAED,qCAAqC;IACrC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;AAC5C,CAAC"}
@@ -1,5 +1,5 @@
1
1
  export { CastThemeProvider, useTheme } from './ThemeProvider';
2
2
  export type { CastThemeProviderProps } from './ThemeProvider';
3
- export { THEME_FONT_FAMILIES, googleFontsUrl } from './fonts';
3
+ export { THEME_FONT_FAMILIES, googleFontsUrl, resolveFont, ANDROID_WEIGHT_SUFFIX } from './fonts';
4
4
  export type { CastTheme, ThemeName, SemanticTokens, SemanticColors, SemanticFontFamily, SemanticFontSize, SemanticFontWeight, SemanticLineHeight, SemanticLetterSpacing, SemanticParagraphSpacing, SemanticParagraphIndent, SemanticBorderRadius, ComponentTokens, ButtonTokens, ButtonVariantTokens, ButtonOutlineTokens, ButtonStateTokens, CardTokens, } from './types';
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC9D,YAAY,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9D,YAAY,EACV,SAAS,EACT,SAAS,EACT,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,wBAAwB,EACxB,uBAAuB,EACvB,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,UAAU,GACX,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC9D,YAAY,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAClG,YAAY,EACV,SAAS,EACT,SAAS,EACT,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,wBAAwB,EACxB,uBAAuB,EACvB,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,UAAU,GACX,MAAM,SAAS,CAAC"}
@@ -1,3 +1,3 @@
1
1
  export { CastThemeProvider, useTheme } from './ThemeProvider';
2
- export { THEME_FONT_FAMILIES, googleFontsUrl } from './fonts';
2
+ export { THEME_FONT_FAMILIES, googleFontsUrl, resolveFont, ANDROID_WEIGHT_SUFFIX } from './fonts';
3
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE9D,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE9D,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@castui/cast-ui",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "A cross-platform design system for React Native (iOS, Android, Web) with multi-theme support.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -17,7 +17,8 @@
17
17
  "build-storybook": "STORYBOOK_DISABLE_TELEMETRY=1 storybook build",
18
18
  "build": "npm run build:tokens && tsc --project tsconfig.build.json",
19
19
  "prepublishOnly": "npm run build",
20
- "test": "echo \"Error: no test specified\" && exit 1"
20
+ "test": "echo \"Error: no test specified\" && exit 1",
21
+ "zh:track-package": "zh-adoption track-package"
21
22
  },
22
23
  "repository": {
23
24
  "type": "git",
@@ -32,7 +33,7 @@
32
33
  "cross-platform",
33
34
  "ui-components"
34
35
  ],
35
- "author": "Connagh <connagh@live.com>",
36
+ "author": "Connagh <connagh@users.noreply.github.com>",
36
37
  "license": "MIT",
37
38
  "type": "commonjs",
38
39
  "bugs": {