@chipmobilesdk/rn-theme 1.0.0 → 1.0.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/CHANGELOG.md CHANGED
@@ -1,4 +1,13 @@
1
- # Changelog
1
+ # @chipmobilesdk/rn-theme
2
+
3
+ ## 1.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Documentation and packaging only; no code change. The README no longer writes a
8
+ version into its header (the changelog is the one place a version lives), the agent
9
+ skill no longer carries one (the consuming app's skill sync stamps the installed
10
+ version), and the changelog goes straight from its title to its releases. ([#15](https://github.com/operationd8979/chip-mobile-sdk/pull/15))
2
11
 
3
12
  ## 1.0.0 - 2026-10-17
4
13
 
package/README.md CHANGED
@@ -7,7 +7,7 @@ Native apps, configured for `react-native-unistyles` 3.
7
7
  > applications; no open-source license is granted. Includes Apache-2.0 code from
8
8
  > material-color-utilities — see `NOTICE`.
9
9
 
10
- - **Version**: `1.0.0`
10
+ - **Version**: see [CHANGELOG](CHANGELOG.md)
11
11
  - **Entrypoint**: `@chipmobilesdk/rn-theme`
12
12
  - **Demo**: `src/screens/ExampleScreen.tsx`
13
13
 
@@ -291,7 +291,7 @@ spreads and casts into `extend`.
291
291
  ## Release Validation
292
292
 
293
293
  ```bash
294
- npm run validate:theme
294
+ npm run validate
295
295
  ```
296
296
 
297
297
  ## Semver and Migration
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chipmobilesdk/rn-theme",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "main": "src/index.ts",
@@ -30,7 +30,7 @@
30
30
  "sideEffects": false,
31
31
  "scripts": {
32
32
  "typecheck": "tsc -p tsconfig.json --noEmit",
33
- "pack:dry-run": "npm pack --dry-run"
33
+ "pack:check": "npm pack --dry-run"
34
34
  },
35
35
  "peerDependencies": {
36
36
  "react": ">=19",
@@ -3,7 +3,6 @@ name: sdk-rn-theme
3
3
  description: "Theming for @chipmobilesdk/rn-theme: accessible brand palettes, Unistyles config, colour/typography/spacing tokens, tones and byTone variants, layout and text presets, app token extension, dark mode and theme mode. Use when setting up theming, writing or shortening a stylesheet, adding a design token, wiring light/dark, or debugging Unistyles setup, stale styles or type errors."
4
4
  metadata:
5
5
  package: "@chipmobilesdk/rn-theme"
6
- packageVersion: "1.0.0"
7
6
  user-invocable: true
8
7
  disable-model-invocation: false
9
8
  ---
@@ -46,7 +45,7 @@ Unistyles 3. Single source of truth for visual tokens; app tokens join through `
46
45
  package) augments `UnistylesThemes` / `UnistylesBreakpoints` from `typeof config`.
47
46
  - Merge Unistyles styles with arrays, never object spread of Unistyles entries.
48
47
 
49
- ## Public API (v1.0.0)
48
+ ## Public API
50
49
 
51
50
  Runtime: `createUnistylesConfig`, `buildThemes`, `applyThemeMode`, `byTone`, `layout`,
52
51
  `presets`, `auditTheme`, `contrastRatio`, `withAlpha`, `toNavigationTheme`,
@@ -107,7 +107,11 @@ function guaranteedPairs(theme: object): ContrastPair[] {
107
107
  const tone = `tone.${name}`;
108
108
  pairs.push(
109
109
  ...onBase(`${tone}.fg`),
110
- { foreground: `${tone}.onContainer`, background: `${tone}.container`, target: TEXT_CONTRAST },
110
+ {
111
+ foreground: `${tone}.onContainer`,
112
+ background: `${tone}.container`,
113
+ target: TEXT_CONTRAST,
114
+ },
111
115
  { foreground: `${tone}.onFill`, background: `${tone}.fill`, target: TEXT_CONTRAST },
112
116
  { foreground: `${tone}.border`, background: 'color.background', target: NON_TEXT_CONTRAST },
113
117
  );
@@ -161,11 +165,7 @@ function channelLuminance(channel: number): number {
161
165
  }
162
166
 
163
167
  function relativeLuminance({ r, g, b }: Rgba): number {
164
- return (
165
- 0.2126 * channelLuminance(r) +
166
- 0.7152 * channelLuminance(g) +
167
- 0.0722 * channelLuminance(b)
168
- );
168
+ return 0.2126 * channelLuminance(r) + 0.7152 * channelLuminance(g) + 0.0722 * channelLuminance(b);
169
169
  }
170
170
 
171
171
  function composite(top: Rgba, bottom: Rgba): Rgba {
@@ -190,7 +190,10 @@ export function contrastRatio(foreground: string, background: string, base?: str
190
190
 
191
191
  const opaqueBackground = composite(bg, composite(baseColor, WHITE));
192
192
  const opaqueForeground = composite(fg, opaqueBackground);
193
- const lighter = Math.max(relativeLuminance(opaqueForeground), relativeLuminance(opaqueBackground));
193
+ const lighter = Math.max(
194
+ relativeLuminance(opaqueForeground),
195
+ relativeLuminance(opaqueBackground),
196
+ );
194
197
  const darker = Math.min(relativeLuminance(opaqueForeground), relativeLuminance(opaqueBackground));
195
198
 
196
199
  return (lighter + LUMINANCE_OFFSET) / (darker + LUMINANCE_OFFSET);
@@ -50,12 +50,11 @@ function parseHex(digits: string): Rgba {
50
50
  const full =
51
51
  digits.length <= 4
52
52
  ? digits
53
- .split('')
54
- .map(digit => digit + digit)
55
- .join('')
53
+ .split('')
54
+ .map(digit => digit + digit)
55
+ .join('')
56
56
  : digits;
57
- const channel = (index: number) =>
58
- parseInt(full.slice(index * 2, index * 2 + 2), 16);
57
+ const channel = (index: number) => parseInt(full.slice(index * 2, index * 2 + 2), 16);
59
58
 
60
59
  return {
61
60
  r: channel(0),
package/src/index.ts CHANGED
@@ -5,10 +5,7 @@ export { ThemeConfigError, ThemeContrastError } from './theme/errors';
5
5
 
6
6
  // ─── Unistyles ──────────────────────────────────────────────────────────────
7
7
  export { createUnistylesConfig } from './unistyles/createUnistylesConfig';
8
- export type {
9
- UnistylesConfig,
10
- UnistylesConfigOptions,
11
- } from './unistyles/createUnistylesConfig';
8
+ export type { UnistylesConfig, UnistylesConfigOptions } from './unistyles/createUnistylesConfig';
12
9
  export { applyThemeMode } from './unistyles/themeMode';
13
10
  export type { ThemeMode, ThemeModeRuntime } from './unistyles/themeMode';
14
11
  export { DEFAULT_BREAKPOINTS } from './tokens/defaults';
@@ -143,7 +143,10 @@ export function buildThemes<
143
143
  TExt extends object = EmptyExtension,
144
144
  TTone extends string = never,
145
145
  TName extends string = never,
146
- >(config: ThemeConfig<TExt, TTone, TName>, options?: BuildThemesOptions): Themes<TExt, TTone, TName> {
146
+ >(
147
+ config: ThemeConfig<TExt, TTone, TName>,
148
+ options?: BuildThemesOptions,
149
+ ): Themes<TExt, TTone, TName> {
147
150
  validateThemeConfig(config);
148
151
 
149
152
  const loose = config as unknown as ThemeConfig<object, string, string>;
@@ -161,7 +164,12 @@ export function buildThemes<
161
164
  tones: { ...loose.tones, ...named.tones } as Record<string, string>,
162
165
  };
163
166
 
164
- return { name, scheme: named.scheme, config: namedConfig, palettes: buildPalettes(namedConfig) };
167
+ return {
168
+ name,
169
+ scheme: named.scheme,
170
+ config: namedConfig,
171
+ palettes: buildPalettes(namedConfig),
172
+ };
165
173
  }),
166
174
  ];
167
175
  const themes: Record<string, object> = {};
@@ -85,7 +85,12 @@ function mergeEntry(entry: Entry): unknown {
85
85
  * keys must exist in `base`, colours must parse, numbers must be finite.
86
86
  * Arrays are replaced whole. `base` itself is never mutated.
87
87
  */
88
- export function mergeOverrides<T>(base: T, overrides: unknown, path: string, segments: string[] = []): T {
88
+ export function mergeOverrides<T>(
89
+ base: T,
90
+ overrides: unknown,
91
+ path: string,
92
+ segments: string[] = [],
93
+ ): T {
89
94
  if (overrides == null) {
90
95
  return base;
91
96
  }
@@ -70,7 +70,10 @@ function validateFontMap(value: unknown, path: string): void {
70
70
 
71
71
  for (const [weight, family] of Object.entries(value)) {
72
72
  if (!FONT_WEIGHT_NAMES.includes(weight as FontWeightName)) {
73
- throw new ThemeConfigError(`${path}.${weight}`, `is not one of ${FONT_WEIGHT_NAMES.join(', ')}.`);
73
+ throw new ThemeConfigError(
74
+ `${path}.${weight}`,
75
+ `is not one of ${FONT_WEIGHT_NAMES.join(', ')}.`,
76
+ );
74
77
  }
75
78
  assertNonEmptyString(family, `${path}.${weight}`);
76
79
  }
@@ -114,7 +117,10 @@ function validateNamedThemes(value: unknown): void {
114
117
  const path = `themes.${name}`;
115
118
 
116
119
  if (RESERVED_THEME_NAMES.has(name)) {
117
- throw new ThemeConfigError(path, 'light and dark are built in; customise them with overrides.');
120
+ throw new ThemeConfigError(
121
+ path,
122
+ 'light and dark are built in; customise them with overrides.',
123
+ );
118
124
  }
119
125
 
120
126
  assertObject(theme, path);
@@ -84,7 +84,9 @@ export function buildFonts(config: TypographyConfig | undefined, platform: Platf
84
84
  // iOS picks weights inside one family, so a partial iOS map only renames
85
85
  // the weights it lists; Android needs a file per weight, hence nearest.
86
86
  const nearest =
87
- platform === 'ios' ? (perWeight?.[weight] && weight) : perWeight && nearestWeight(weight, perWeight);
87
+ platform === 'ios'
88
+ ? perWeight?.[weight] && weight
89
+ : perWeight && nearestWeight(weight, perWeight);
88
90
 
89
91
  if (!perWeight || !nearest) {
90
92
  return { fontFamily: family, fontWeight: FONT_WEIGHTS[weight] };
@@ -117,7 +119,8 @@ export function buildTypography(
117
119
  font: Record<FontWeightName, FontToken>,
118
120
  family: string,
119
121
  ): Typography {
120
- const scale = ((config?.baseFontSize ?? DEFAULT_BODY_SIZE) / DEFAULT_BODY_SIZE) * (config?.fontScale ?? 1);
122
+ const scale =
123
+ ((config?.baseFontSize ?? DEFAULT_BODY_SIZE) / DEFAULT_BODY_SIZE) * (config?.fontScale ?? 1);
121
124
  const typography = { fontFamily: family } as Typography;
122
125
 
123
126
  for (const role of Object.keys(SCALE) as TypographyRole[]) {
@@ -2,13 +2,7 @@ export type ColorScheme = 'light' | 'dark';
2
2
 
3
3
  /** Colours whose roles come in sets of four: X, onX, XContainer, onXContainer. */
4
4
  export type AccentRoleName =
5
- | 'primary'
6
- | 'secondary'
7
- | 'tertiary'
8
- | 'error'
9
- | 'success'
10
- | 'warning'
11
- | 'info';
5
+ 'primary' | 'secondary' | 'tertiary' | 'error' | 'success' | 'warning' | 'info';
12
6
 
13
7
  export interface ColorRoles {
14
8
  primary: string;
@@ -69,13 +63,7 @@ export interface ColorRoles {
69
63
  disabled: string;
70
64
  }
71
65
 
72
- export type StandardToneName =
73
- | 'neutral'
74
- | 'primary'
75
- | 'info'
76
- | 'success'
77
- | 'warning'
78
- | 'error';
66
+ export type StandardToneName = 'neutral' | 'primary' | 'info' | 'success' | 'warning' | 'error';
79
67
 
80
68
  /** A matched colour set for one tone, for use with `byTone`. */
81
69
  export interface ToneColors {
@@ -114,8 +114,10 @@ export interface BaseTheme<TTone extends string = never> {
114
114
 
115
115
  export type EmptyExtension = Record<never, never>;
116
116
 
117
- export type Theme<TExt extends object = EmptyExtension, TTone extends string = never> = BaseTheme<TTone> &
118
- TExt;
117
+ export type Theme<
118
+ TExt extends object = EmptyExtension,
119
+ TTone extends string = never,
120
+ > = BaseTheme<TTone> & TExt;
119
121
 
120
122
  export type ThemeName<TName extends string = never> = 'light' | 'dark' | TName;
121
123
 
@@ -32,9 +32,7 @@ export interface TypographyConfig {
32
32
  export type TypographyRole = 'display' | 'headline' | 'title' | 'body' | 'label' | 'caption';
33
33
 
34
34
  export type TypographyStyleName =
35
- | TypographyRole
36
- | `${TypographyRole}Small`
37
- | `${TypographyRole}Large`;
35
+ TypographyRole | `${TypographyRole}Small` | `${TypographyRole}Large`;
38
36
 
39
37
  export interface TypographyToken {
40
38
  fontSize: number;
@@ -21,15 +21,15 @@ export type UnistylesConfigOptions<
21
21
  breakpoints?: TBreakpoints;
22
22
  } & (
23
23
  | {
24
- /** Theme active on first launch. Defaults to 'light'. */
25
- initialTheme?: ThemeName<TName> | (() => ThemeName<TName>);
26
- adaptiveThemes?: false;
27
- }
24
+ /** Theme active on first launch. Defaults to 'light'. */
25
+ initialTheme?: ThemeName<TName> | (() => ThemeName<TName>);
26
+ adaptiveThemes?: false;
27
+ }
28
28
  | {
29
- /** Follow the system colour scheme between light and dark. */
30
- adaptiveThemes: boolean;
31
- initialTheme?: never;
32
- }
29
+ /** Follow the system colour scheme between light and dark. */
30
+ adaptiveThemes: boolean;
31
+ initialTheme?: never;
32
+ }
33
33
  );
34
34
 
35
35
  type UnistylesSettings<TName extends string> = UnistylesBaseSettings &
@@ -103,7 +103,11 @@ function validateOptions(options: unknown, themeNames: string[]): void {
103
103
  throw new ThemeConfigError('options.CSSVars', 'must be a boolean.');
104
104
  }
105
105
 
106
- if (nativeBreakpointsMode != null && nativeBreakpointsMode !== 'pixels' && nativeBreakpointsMode !== 'points') {
106
+ if (
107
+ nativeBreakpointsMode != null &&
108
+ nativeBreakpointsMode !== 'pixels' &&
109
+ nativeBreakpointsMode !== 'points'
110
+ ) {
107
111
  throw new ThemeConfigError('options.nativeBreakpointsMode', "must be 'pixels' or 'points'.");
108
112
  }
109
113