@adea-ai/themes 0.2.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/src/sources.ts CHANGED
@@ -37,6 +37,7 @@
37
37
  */
38
38
 
39
39
  import type { Base24Slot } from './adapters/base24'
40
+ import { CONTRAST_FLOORS } from './normalize'
40
41
  import type { ThemeAppearance, ThemeProvenance } from './schema'
41
42
 
42
43
  /** The dataset the Base24 schemes are reproduced from. */
@@ -132,16 +133,48 @@ export interface ComposedSource {
132
133
  /** The role supplying the accent, when the family's identity demands one. */
133
134
  accentSlot?: 'blue' | 'magenta' | 'cyan' | 'green'
134
135
  palette?: Partial<Record<Base24Slot, string>>
136
+ /**
137
+ * Transpose this composition's hues onto the borrowed canvas.
138
+ *
139
+ * Set when a composition takes its hues from a palette authored against a different
140
+ * kind of canvas — which is what borrowing a *dark* palette's hues for a *light*
141
+ * canvas is. See `transposeHues` in `src/normalize.ts` for why the whole hue set
142
+ * moves by one common step rather than each hue being repaired on its own.
143
+ */
144
+ hueTranspose?: { floor: number }
135
145
  provenance: ThemeProvenance
136
146
  }
137
147
 
138
148
  /**
139
- * Adea's composed dark theme.
149
+ * Adea's two default themes.
150
+ *
151
+ * Both are compositions and both were previously authored here — the light one as a
152
+ * neutral grey ramp on white with a monochrome accent, the dark one as a neutral grey
153
+ * ramp on `#252525`. They replaced those entirely while keeping their names, because
154
+ * they are the defaults: the system's own appearance, not a pair of options beside it.
155
+ *
156
+ * ## Why they are a *pair* rather than two themes
140
157
  *
141
- * This was previously authored here as a neutral grey ramp with a monochrome accent.
142
- * It is now a composition, so the default dark theme is derived from named upstream
143
- * palettes and re-derivable, and it gains the saturated hues the earlier ramp did not
144
- * have.
158
+ * They share one hue donor, so a red is the same red and a blue the same blue in both,
159
+ * and each borrows its canvas from a palette chosen for the *canvas* rather than for
160
+ * its colours:
161
+ *
162
+ * | | canvas | canvas hue | hues |
163
+ * | --- | --- | --- | --- |
164
+ * | Adea Dark | Aardvark Ink `#0f141f` | 265° | GitHub Dark Default |
165
+ * | Adea Light | Nord Light `#e3e9f4` | 262° | GitHub Dark Default |
166
+ *
167
+ * Three degrees apart, and both canvases are *tinted* rather than neutral — 0.024 and
168
+ * 0.016 of chroma — which is the property that makes the pair feel like one theme seen
169
+ * at two exposures instead of two themes that happen to ship together. Switching
170
+ * appearance changes lightness and nothing else about the theme's identity; that claim
171
+ * is asserted in `tests/provenance.test.ts` rather than left as intent.
172
+ *
173
+ * The light theme is the reason `hueTranspose` exists: GitHub's dark hues measure
174
+ * between 2.5:1 and 4.1:1 on a light canvas, so the whole hue set is transposed down in
175
+ * lightness by one common step — hue and chroma intact, relative brightness ordering
176
+ * intact — rather than repaired hue by hue, which would have collapsed every bright
177
+ * variant onto its normal sibling.
145
178
  */
146
179
  export const COMPOSED_SOURCES: readonly ComposedSource[] = Object.freeze([
147
180
  {
@@ -205,6 +238,77 @@ export const COMPOSED_SOURCES: readonly ComposedSource[] = Object.freeze([
205
238
  ],
206
239
  },
207
240
  },
241
+ {
242
+ id: 'adea-light',
243
+ name: 'Adea Light',
244
+ family: 'adea',
245
+ familyLabel: 'Adea',
246
+ label: 'Light',
247
+ description:
248
+ "The default light theme. Nord Light's cool canvas, with the same hues as Adea Dark.",
249
+ appearance: 'light',
250
+ tags: ['light', 'default', 'cool'],
251
+ donors: { structure: 'nord-light', hues: 'github-dark-default' },
252
+ slots: {
253
+ // The structure donor. Same shape as the dark theme's, with one difference worth
254
+ // noting: Nord Light's `black` (`#3b4252`) is *darker* than its foreground, which
255
+ // is what an ANSI black should be on a light canvas — the text colour and the
256
+ // darkest neutral are the same role there, and Base24's `base01` is where Nord
257
+ // keeps it.
258
+ base00: ['structure', 'background'],
259
+ base01: ['structure', 'black'],
260
+ base02: ['structure', 'selection'],
261
+ base03: ['structure', 'brightBlack'],
262
+ base04: ['structure', 'white'],
263
+ base05: ['structure', 'foreground'],
264
+ base06: ['structure', 'brightWhite'],
265
+ base07: ['structure', 'brightWhite'],
266
+ base08: ['hues', 'red'],
267
+ base0A: ['hues', 'yellow'],
268
+ base0B: ['hues', 'green'],
269
+ base0C: ['hues', 'cyan'],
270
+ base0D: ['hues', 'blue'],
271
+ base0E: ['hues', 'purple'],
272
+ base12: ['hues', 'brightRed'],
273
+ base13: ['hues', 'brightYellow'],
274
+ base14: ['hues', 'brightGreen'],
275
+ base15: ['hues', 'brightCyan'],
276
+ base16: ['hues', 'brightBlue'],
277
+ base17: ['hues', 'brightPurple'],
278
+ },
279
+ synthesise: {
280
+ base09: ['hues', 'red', { rotate: 26 }],
281
+ base0F: ['hues', 'yellow', { rotate: -34 }],
282
+ // Beyond the canvas rather than below it, which is the convention for a light
283
+ // scheme: these two slots are the light end of the ramp.
284
+ base10: ['structure', 'background', { lighten: 0.03 }],
285
+ base11: ['structure', 'background', { lighten: 0.06 }],
286
+ },
287
+ /**
288
+ * The one deliberate deviation from the donor, and it is aesthetic rather than
289
+ * factual — unlike the One Dark correction above, nothing here is wrong.
290
+ *
291
+ * Nord Light's canvas carries 0.010 of chroma against Aardvark Ink's 0.024, so
292
+ * rendering the same hue at half the tint makes the light theme read as neutral grey
293
+ * beside a partner that is clearly navy. Deepening it to 0.016 — two thirds of the
294
+ * dark theme's — is what makes the pair look like siblings. Lightness and hue are
295
+ * untouched, so no contrast pairing moves: body text measures 7.5:1 either way.
296
+ */
297
+ palette: { base00: 'oklch(0.933 0.016 261.79)' },
298
+ ansiFromStructure: ['black', 'brightBlack', 'white', 'brightWhite'],
299
+ // GitHub's hues were drawn for a `#0d1117` canvas. On Nord Light's they measure
300
+ // between 2.5:1 and 4.1:1, which is legible as an accent and not as terminal text.
301
+ hueTranspose: { floor: CONTRAST_FLOORS.status },
302
+ provenance: {
303
+ project: 'Adea',
304
+ url: 'https://github.com/adea-ai/themes',
305
+ license: 'Apache-2.0',
306
+ bootstrappedFrom: [
307
+ 'Nord Light (iTerm2-Color-Schemes)',
308
+ 'GitHub Dark Default (iTerm2-Color-Schemes)',
309
+ ],
310
+ },
311
+ },
208
312
  ])
209
313
 
210
314
  /**
@@ -654,9 +758,17 @@ export const VENDORED_SOURCES: readonly VendoredSource[] = Object.freeze([
654
758
  * accent — the design system's default has to work as a default for everyone, which
655
759
  * means it cannot be anybody's favourite colour.
656
760
  *
657
- * Only the light variant is authored here. Its dark counterpart was previously the
658
- * second entry in this list and is now a {@link ComposedSource} built from two
659
- * upstream palettes, which is where the default dark theme gets its saturated hues.
761
+ * Intentionally empty, and worth leaving that way until something needs it.
762
+ *
763
+ * This was where both default themes lived. They are now
764
+ * {@link COMPOSED_SOURCES} entries instead, which is a better home for them: a
765
+ * composition names the upstream palettes it came from and can be rebuilt when either
766
+ * moves, while an authored entry is a table of values nobody can refresh.
767
+ *
768
+ * The route stays because it is the only way to specify *semantic* roles directly
769
+ * rather than deriving them through Base24 slots — which is what a brand theme with
770
+ * values handed over by a designer would need. Nothing uses it today; if nothing has by
771
+ * the time someone reads this, delete it.
660
772
  */
661
773
  export interface AuthoredSource {
662
774
  id: string
@@ -724,55 +836,4 @@ export interface AuthoredSource {
724
836
  selection: string
725
837
  }
726
838
 
727
- export const AUTHORED_SOURCES: readonly AuthoredSource[] = Object.freeze([
728
- {
729
- id: 'adea-light',
730
- family: 'adea',
731
- familyLabel: 'Adea',
732
- label: 'Light',
733
- name: 'Adea Light',
734
- description: "The default light theme. A neutral ladder with a monochrome accent.",
735
- appearance: 'light',
736
- tags: ['light', 'neutral', 'default'],
737
- ramp: {
738
- background: '#ffffff',
739
- foreground: '#252525',
740
- surface: '#ffffff',
741
- surfaceElevated: '#ffffff',
742
- surfaceHover: '#f7f7f7',
743
- surfaceActive: '#f0f0f0',
744
- border: '#ebebeb',
745
- borderMuted: '#f2f2f2',
746
- textMuted: '#6f6f6f',
747
- textSubtle: '#767676',
748
- },
749
- accent: '#343434',
750
- accentForeground: '#fcfcfc',
751
- status: {
752
- success: '#1a7f37',
753
- warning: '#a16207',
754
- error: '#c53c2b',
755
- info: '#0e7490',
756
- },
757
- ansi: {
758
- black: '#1b1f24',
759
- red: '#b91c1c',
760
- green: '#116a2e',
761
- yellow: '#8a5a1b',
762
- blue: '#0b57d0',
763
- magenta: '#a0186f',
764
- cyan: '#0e7490',
765
- white: '#57606a',
766
- brightBlack: '#57606a',
767
- brightRed: '#c94d4d',
768
- brightGreen: '#1f9d4f',
769
- brightYellow: '#a9752c',
770
- brightBlue: '#3b82f6',
771
- brightMagenta: '#c04a92',
772
- brightCyan: '#0891b2',
773
- brightWhite: '#24292f',
774
- },
775
- cursor: '#24292f',
776
- selection: '#b6c7ff',
777
- },
778
- ])
839
+ export const AUTHORED_SOURCES: readonly AuthoredSource[] = Object.freeze([])