@keboola/tailwind-config 1.1.2 → 1.2.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @keboola/tailwind-config
2
2
 
3
+ ## 1.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - The reading rhythm of an answer is one definition
8
+ - `tw:prose`'s `--tw-prose-*` slots point at theme tokens, so prose follows the colour scheme and the brand without a call site reaching for `prose-invert`. The `[data-theme='dark'] .tw:prose → prose-invert` patch is gone with them. This is not additive: every `.tw:prose` surface takes the new palette, tooltips and docs included.
9
+ - `tw:prose-answer` is the new measure, heading scale, heading margins and paragraph/list rhythm a Keboola answer reads on. `AIKit/Markdown` wears it, so every chat gets the same one.
10
+ - `Markdown` takes a `rhythm` prop: `answer` (the default) is the new rhythm, `chrome` is `tw:prose` on the dense `tw:prose-sm` scale for content a model did not write — a dashboard text widget, a vendor's app description.
11
+ - `tw:prose-code-chip` paints inline code as a chip on the `--muted` / `--foreground` pair, and both rhythms wear it. It is its own utility because it is palette, not rhythm: without it a surface keeps Typography's pinned `#c7254e`, which measures 3.36:1 on the dark page.
12
+ - `Markdown.Root` / `Markdown.Block` split one prose surface from the N blocks a streaming renderer paints into it — the rhythm then resolves `:first-child` / `:last-child` across the whole reply.
13
+ - `tw:prose-answer`'s wider values apply inside an `@container/messages` at 640px and up; where no consumer declares that container it keeps its narrow ones.
14
+
3
15
  ## 1.1.2
4
16
 
5
17
  ### Patch Changes
package/colors.ts CHANGED
@@ -1,6 +1,4 @@
1
- // Keboola brand ramps. Historically these lived under `primary[100..900]` (green)
2
- // and `secondary[100..900]` (blue); defined once here so the legacy `primary`/
3
- // `secondary` aliases and the semantic-correct `green`/`blue` palettes can't drift.
1
+ // Shared by the `primary`/`secondary` aliases and the `green`/`blue` palettes so they can't drift.
4
2
  const greenScale = {
5
3
  100: '#e0ffe0',
6
4
  200: '#baf5ba',
@@ -35,12 +33,12 @@ export default {
35
33
  preCodeBg: '#f5f5f5',
36
34
  },
37
35
 
38
- // Main application colors — channel values live in CSS variables (see packages/design/src/styles.css :root).
39
- // The `<alpha-value>` placeholder lets Tailwind apply opacity modifiers like `tw:bg-muted/50`.
36
+ // Channel values live in CSS vars (packages/design/src/styles.css :root).
37
+ // `<alpha-value>` enables opacity modifiers like `tw:bg-muted/50`.
40
38
  background: 'rgb(var(--color-background) / <alpha-value>)',
41
39
  foreground: 'rgb(var(--color-foreground) / <alpha-value>)',
42
40
 
43
- // Raised container chrome (Modal/Card/Sheet). White in light, lifted off background in dark.
41
+ // Raised container chrome (Modal/Card/Sheet).
44
42
  card: {
45
43
  DEFAULT: 'rgb(var(--color-card) / <alpha-value>)',
46
44
  foreground: 'rgb(var(--color-card-foreground) / <alpha-value>)',
@@ -59,15 +57,13 @@ export default {
59
57
 
60
58
  border: 'rgb(var(--color-border) / <alpha-value>)',
61
59
 
62
- // `DEFAULT` is the input *border*; `background` is the control's own fill.
63
- // Both are their own token because a form control is not a card — `card` is
64
- // raised container chrome and re-skins independently of the field surface.
60
+ // `DEFAULT` is the input border; `background` its fill.
61
+ // Not `card`: a form control re-skins independently of container chrome.
65
62
  input: {
66
63
  DEFAULT: 'rgb(var(--color-input) / <alpha-value>)',
67
64
  background: 'rgb(var(--color-input-background) / <alpha-value>)',
68
65
  },
69
66
 
70
- // Focus ring (brand blue).
71
67
  ring: 'rgb(var(--color-ring) / <alpha-value>)',
72
68
 
73
69
  // Focus-ring offset gap (the surface color the ring is inset against).
@@ -100,19 +96,13 @@ export default {
100
96
  foreground: 'rgb(var(--color-muted-foreground) / <alpha-value>)',
101
97
  },
102
98
 
103
- // Loading-placeholder fill (Skeleton). Darker than `muted` in light, lighter
104
- // than card/background in dark, so the block is perceptible in both schemes.
99
+ // Skeleton fill; perceptible against `muted` and surfaces in both schemes.
105
100
  skeleton: 'rgb(var(--color-skeleton) / <alpha-value>)',
106
101
 
107
- // Table header surface — a chrome tint that sits clearly above the table
108
- // body (card/background) in both schemes. `muted` is ~1.05:1 vs surfaces, so
109
- // the header row was near-invisible; this separates it. Scheme-aware only
110
- // (light fallback here + `[data-theme='dark']` override in styles.css) — like
111
- // `--color-skeleton`, it is not a brand-registry `Colors` slot, so brands
112
- // don't re-skin it; a consumer can still override `--color-table-header`.
102
+ // Table header tint (`muted` is too close to surfaces); dark override in styles.css.
103
+ // Not a brand `Colors` slot, so brands don't re-skin it.
113
104
  'table-header': 'rgb(var(--color-table-header, 219 225 235) / <alpha-value>)',
114
105
 
115
- // Sidebar semantic colors
116
106
  sidebar: {
117
107
  DEFAULT: 'rgb(var(--color-sidebar) / <alpha-value>)',
118
108
  foreground: 'rgb(var(--color-sidebar-foreground) / <alpha-value>)',
@@ -228,14 +218,8 @@ export default {
228
218
  900: '#5C0A54',
229
219
  },
230
220
 
231
- // Semantic-correct names for the Keboola brand ramps that historically lived under
232
- // `primary[100..900]` (green) and `secondary[100..900]` (blue) — fixed tints used
233
- // directly regardless of brand, unlike `primary.DEFAULT` / `secondary.DEFAULT`
234
- // (shadcn semantic, CSS-var driven). New code should use these; the `primary`/
235
- // `secondary` ramps remain as aliases until their callsites are migrated (Step 2).
236
- //
237
- // Like the existing custom palettes (`cyan`/`teal`/`yellow`/`pink`/`purple`), these
238
- // are `100..900` only and override Tailwind's same-named defaults.
221
+ // Fixed brand-independent ramps; prefer over the `primary`/`secondary` ramp aliases.
222
+ // `100..900` only; overrides Tailwind's same-named defaults.
239
223
  green: { ...greenScale },
240
224
  blue: { ...blueScale },
241
225
  };
package/index.js CHANGED
@@ -2,10 +2,8 @@ import colors from './colors';
2
2
  import { typography } from './typography';
3
3
 
4
4
  /**
5
- * The Keboola token surface as a Tailwind `theme`, loaded through v4's `@config`
6
- * bridge (see `packages/design/src/styles.css`). Everything a v4 build owns in
7
- * CSS — the class prefix, `@import`ed layers, the scoped preflight, plugins — is
8
- * intentionally absent: declare those in your stylesheet, not here.
5
+ * The Keboola token surface as a Tailwind `theme`, loaded via v4's `@config` (see `packages/design/src/styles.css`).
6
+ * Prefix, layers, preflight and plugins belong in the stylesheet, not here.
9
7
  *
10
8
  * @type {Pick<import('tailwindcss').Config, 'theme'>}
11
9
  */
@@ -18,9 +16,7 @@ const config = {
18
16
  700: 'url(#aigradient-700)',
19
17
  },
20
18
  },
21
- // Lucide icons are stroke-based — exposing the AI gradient as a
22
- // stroke utility lets us paint the visible line with the gradient,
23
- // not just the (often empty) interior fill area.
19
+ // Lucide icons are stroke-based, so the gradient must paint the stroke, not just the fill.
24
20
  stroke: {
25
21
  aigradient: {
26
22
  600: 'url(#aigradient-600)',
@@ -37,20 +33,15 @@ const config = {
37
33
  'dot-pulse-2': 'dot-pulse 1.5s infinite 0.2s',
38
34
  'dot-pulse-3': 'dot-pulse 1.5s infinite 0.4s',
39
35
  'toast-progress': 'toast-progress 5s linear forwards',
40
- // `collapsible-down` / `collapsible-up` are absent on purpose: `tw-animate-css` ships both
41
- // utilities and wins, so an entry here emits nothing. `Collapsible`'s motion is registered
42
- // as `collapsible-open` / `collapsible-close` in `packages/design/src/theme.css`.
36
+ // No `collapsible-down`/`-up`: `tw-animate-css` ships them and wins.
37
+ // `Collapsible` uses `collapsible-open`/`-close` from `packages/design/src/theme.css`.
43
38
  'marching-ants': 'marching-ants 1s linear infinite',
44
39
  'spinner-dots': 'spinner-dots 1s ease-in-out infinite',
45
40
  'progress-bar': 'progress-bar var(--progress-bar-duration, 3s) linear forwards',
46
41
  },
47
42
  keyframes: {
48
- // Animate the `translate` property, NOT `transform`. In v4 the `-translate-x-*`
49
- // utilities (Skeleton's `before:-translate-x-full` start position) set the CSS
50
- // `translate` property, not `transform`. A `transform`-based keyframe composes
51
- // as a SEPARATE property, so the static `translate:-100%` pins the shimmer
52
- // off-screen and the sweep is invisible. Matching `translate` lets the keyframe
53
- // override the start position and sweep -100% -> 100% as before.
43
+ // Animates `translate`, not `transform`: v4's `-translate-x-*` (Skeleton's start) sets
44
+ // `translate`, which a `transform` keyframe can't override, hiding the sweep.
54
45
  shimmer: {
55
46
  '100%': {
56
47
  translate: '100% 0',
@@ -93,9 +84,8 @@ const config = {
93
84
  },
94
85
  backgroundImage: {
95
86
  'loader-gradient': 'radial-gradient(circle closest-side,#000 90%,#0000)',
96
- // Brand gradient — `tw:bg-brand`. Set via `theme.gradient.brand`; the
97
- // inline fallback equals the stylesheet default (primary→accent), so the
98
- // utility stays valid even without `@keboola/design/styles`.
87
+ // `tw:bg-brand`; the fallback equals the stylesheet default, so it works
88
+ // without `@keboola/design/styles`.
99
89
  brand:
100
90
  'var(--gradient-brand, linear-gradient(135deg, rgb(var(--color-primary)), rgb(var(--color-accent, var(--color-primary)))))',
101
91
  },
@@ -106,10 +96,7 @@ const config = {
106
96
  outline: '0 0 0 3px rgba(34, 141, 255, 0.25)',
107
97
  table: '0px 6px 6px -4px rgba(100, 135, 153, 0.13)',
108
98
  card: '0px 1px 4px rgba(100, 135, 153, 0.15)',
109
- // Brand-driven elevation. The inline fallbacks equal the stock Tailwind
110
- // shadow scale, so `tw:shadow-sm`, `tw:shadow-md` and `tw:shadow-lg`
111
- // are unchanged until a brand sets `theme.shadows`. `tw:shadow-lift`
112
- // is a stronger raised shadow.
99
+ // Fallbacks equal stock Tailwind shadows until a brand sets `theme.shadows`.
113
100
  sm: 'var(--shadow-sm, 0 1px 2px 0 rgb(0 0 0 / 0.05))',
114
101
  md: 'var(--shadow-md, 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1))',
115
102
  lg: 'var(--shadow-lg, 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1))',
@@ -117,12 +104,7 @@ const config = {
117
104
  },
118
105
  borderRadius: {
119
106
  card: '10px',
120
- // Brand-driven corner radii — a brand overrides via `theme.radii`, which
121
- // `ThemeProvider` emits as `--radius-*`. The inline fallbacks equal the
122
- // stock Tailwind scale, so `tw:rounded-sm`, `tw:rounded-md` and
123
- // `tw:rounded-lg` are unchanged until a brand (or the design
124
- // stylesheet's `:root`) sets the var. Mirrors the
125
- // `shadow-*` / `space-*` pattern above.
107
+ // `theme.radii` via `ThemeProvider`; fallbacks equal the stock Tailwind scale.
126
108
  sm: 'var(--radius-sm, 0.125rem)',
127
109
  md: 'var(--radius-md, 0.375rem)',
128
110
  lg: 'var(--radius-lg, 0.5rem)',
@@ -140,12 +122,8 @@ const config = {
140
122
  },
141
123
  typography: typography,
142
124
  colors,
143
- // Brand-driven type scale — semantic `text-display` / `text-heading` /
144
- // `text-body` utilities that bundle size + line-height + weight + tracking
145
- // into one step. Each axis reads a `--type-*` var (emitted by ThemeProvider
146
- // from `typography.typeScale`) with an inline fallback, so the utilities
147
- // stay valid and unchanged until a brand opts in. Under `extend` so
148
- // Tailwind's default `text-sm` / `text-xl` / … scale is preserved.
125
+ // `--type-*` from `typography.typeScale` via ThemeProvider; fallbacks keep it valid when unset.
126
+ // Under `extend` to keep Tailwind's default `text-*` scale.
149
127
  fontSize: {
150
128
  display: [
151
129
  'var(--type-display-size, 1.875rem)',
@@ -172,22 +150,14 @@ const config = {
172
150
  },
173
151
  ],
174
152
  },
175
- // Chrome tracking token — under `extend` so Tailwind's default tracking
176
- // scale (`tracking-normal` / `tracking-wide` / …) is preserved. `chrome`
177
- // reads `--letter-spacing-chrome` (from `typography.letterSpacing.chrome`),
178
- // falling back to the current 1px value.
153
+ // Under `extend` to keep Tailwind's default tracking scale.
179
154
  letterSpacing: {
180
155
  chrome: 'var(--letter-spacing-chrome, 1px)',
181
156
  },
182
157
  },
183
158
  fontFamily: {
184
- // Display face for headings — `tw:font-display`. Set via
185
- // `theme.typography.fontDisplay`; when unset it resolves to `--font-sans`
186
- // then the Inter stack, so it defaults to the body face rather than a
187
- // generic `sans-serif`. The fallback chain lives *inside* the `var()` so
188
- // the declaration stays valid when the vars are undefined — a bare
189
- // undefined `var()` invalidates the whole `font-family` at
190
- // computed-value time, and the later families would not rescue it.
159
+ // `theme.typography.fontDisplay`, else the body face. Fallbacks live inside `var()`:
160
+ // a bare undefined `var()` invalidates the whole `font-family`.
191
161
  display: [
192
162
  'var(--font-display, var(--font-sans, Inter))',
193
163
  'system-ui',
@@ -214,11 +184,7 @@ const config = {
214
184
  'Courier New',
215
185
  'monospace',
216
186
  ],
217
- // Body face — `tw:font-sans`. A brand's `typography.fontFamily` is emitted
218
- // as `--font-sans` by `ThemeProvider`; the inline `Inter` fallback keeps
219
- // the canonical stack when unset, so default rendering is unchanged. The
220
- // fallback lives *inside* the `var()` (same reasoning as `display` above)
221
- // so the declaration stays valid when the var is undefined.
187
+ // `typography.fontFamily` via `ThemeProvider`; fallback inside `var()` as for `display`.
222
188
  sans: [
223
189
  'var(--font-sans, Inter)',
224
190
  'system-ui',
@@ -236,9 +202,7 @@ const config = {
236
202
  'Noto Color Emoji',
237
203
  ],
238
204
  },
239
- // Chrome font-weight scale. Brand-overridable via `--font-weight-*` (emitted
240
- // by ThemeProvider from `typography.weight`); fallbacks are the design
241
- // defaults, so a brand that omits weights shows no drift.
205
+ // `--font-weight-*` from `typography.weight` via ThemeProvider; fallbacks are design defaults.
242
206
  fontWeight: {
243
207
  normal: 'var(--font-weight-normal, 400)',
244
208
  medium: 'var(--font-weight-medium, 500)',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keboola/tailwind-config",
3
- "version": "1.1.2",
3
+ "version": "1.2.0",
4
4
  "description": "Shared Tailwind CSS v4 design tokens for the Keboola UI platform — semantic color palette, type scale, radii and shadows as a `theme` object.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/typography.ts CHANGED
@@ -6,23 +6,8 @@ export const typography = {
6
6
  'h1, h2, h3, h4, h5, h6': {
7
7
  color: 'inherit',
8
8
  },
9
- // Code in a quote or a fenced block takes the ink of the surface it sits
10
- // on rather than a pinned one.
11
- //
12
- // - `--tw-prose-code` is `#c7254e` in both schemes; the fill under it is
13
- // not. On a dark `--muted` slab that pin measures 2.72:1.
14
- // - A colour declared here lands on the `<code>`, so it beats the one a
15
- // call site puts on the `<pre>` — inheritance loses to any declaration.
16
- // - Raising the call site's specificity is not an escape either: under
17
- // `[data-theme='dark'] .tw\:prose` this rule is `(0,2,0)`, above the
18
- // `(0,1,2)` of a `[&_pre_code]:…` arbitrary variant.
19
- // - Bare inline `code` is deliberately not in the list. It keeps
20
- // `--tw-prose-invert-code`, 3.36:1 on the dark page. The chat surfaces
21
- // that render most of it put it on their own chip — `AIKit/Markdown`
22
- // and kbc-ui's chat both do. A bare `.tw\:prose` does not, and
23
- // kbc-ui's `react/common/Markdown` is one: there the 3.36:1 stands.
24
- // The fix still belongs with the chip — a rule here would strip the
25
- // accent from every call site at once, including the ones that want it.
9
+ // Quoted/fenced code inherits surface ink: pinned `--tw-prose-code` is 2.72:1 on dark `--muted`.
10
+ // Beats call-site colours on `<pre>`. Inline `code` is excluded: its chip belongs to the call site.
26
11
  'blockquote code, pre code': {
27
12
  color: 'inherit',
28
13
  },
@@ -30,9 +15,23 @@ export const typography = {
30
15
  },
31
16
  DEFAULT: {
32
17
  css: {
18
+ // Re-points the plugin's fixed light palette at theme tokens so prose follows scheme and brand.
19
+ // Raw `--<token>` triples: `--color-*` is `@theme inline` and doesn't exist at runtime.
20
+ '--tw-prose-body': 'rgb(var(--foreground))',
21
+ '--tw-prose-headings': 'rgb(var(--foreground))',
22
+ '--tw-prose-bold': 'rgb(var(--foreground))',
23
+ '--tw-prose-quotes': 'rgb(var(--foreground))',
24
+ '--tw-prose-links': 'rgb(var(--primary))',
25
+ '--tw-prose-counters': 'rgb(var(--muted-foreground))',
26
+ '--tw-prose-bullets': 'rgb(var(--muted-foreground))',
27
+ '--tw-prose-captions': 'rgb(var(--muted-foreground))',
28
+ '--tw-prose-hr': 'rgb(var(--border))',
29
+ '--tw-prose-th-borders': 'rgb(var(--border))',
30
+ '--tw-prose-td-borders': 'rgb(var(--border))',
31
+ // Inline code keeps its accent; chip surfaces opt in with `tw:prose-answer`.
33
32
  '--tw-prose-code': colors.prose.code,
34
- '--tw-prose-pre-code': colors.prose.preCode,
35
- '--tw-prose-pre-bg': colors.prose.preCodeBg,
33
+ '--tw-prose-pre-code': 'rgb(var(--foreground))',
34
+ '--tw-prose-pre-bg': 'rgb(var(--muted))',
36
35
  '--tw-prose-invert-body': colors.white,
37
36
  '--tw-prose-invert-code': colors.prose.code,
38
37
  '--tw-prose-invert-pre-code': colors.prose.preCode,
@@ -42,11 +41,13 @@ export const typography = {
42
41
  maxWidth: 'none',
43
42
  fontSize: '14px',
44
43
  lineHeight: '1.42857143',
44
+ // Each heading must name `--tw-prose-headings`: it overrides the plugin's colour rule.
45
+ // Not `rgb(var(--color-foreground))`: that var is `@theme inline` and already a full `rgb()`.
45
46
  h1: {
46
47
  fontSize: '32px',
47
48
  lineHeight: '40px',
48
49
  fontWeight: '500',
49
- color: 'rgb(var(--color-foreground))',
50
+ color: 'var(--tw-prose-headings)',
50
51
  marginTop: 0,
51
52
  marginBottom: '10px',
52
53
  },
@@ -57,7 +58,7 @@ export const typography = {
57
58
  fontSize: '24px',
58
59
  lineHeight: '32px',
59
60
  fontWeight: '500',
60
- color: 'rgb(var(--color-foreground))',
61
+ color: 'var(--tw-prose-headings)',
61
62
  marginTop: 0,
62
63
  marginBottom: '10px',
63
64
  },
@@ -68,25 +69,25 @@ export const typography = {
68
69
  fontSize: '16px',
69
70
  lineHeight: '24px',
70
71
  fontWeight: '500',
71
- color: 'rgb(var(--color-foreground))',
72
+ color: 'var(--tw-prose-headings)',
72
73
  margin: 0,
73
74
  },
74
75
  h4: {
75
76
  fontSize: '14px',
76
77
  lineHeight: '20px',
77
78
  fontWeight: '500',
78
- color: 'rgb(var(--color-foreground))',
79
+ color: 'var(--tw-prose-headings)',
79
80
  margin: 0,
80
81
  },
81
82
  h5: {
82
83
  fontSize: '12px',
83
84
  lineHeight: '20px',
84
85
  fontWeight: '500',
85
- color: 'rgb(var(--color-foreground))',
86
+ color: 'var(--tw-prose-headings)',
86
87
  margin: 0,
87
88
  },
88
89
  h6: {
89
- color: 'rgb(var(--color-foreground))',
90
+ color: 'var(--tw-prose-headings)',
90
91
  margin: 0,
91
92
  },
92
93
  'h1 small, h2 small, h3 small, h4 small, h5 small, h6 small': {