@glw907/cairn-cms 0.52.0 → 0.53.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.
@@ -31,6 +31,9 @@ through the adapter's render. Swapping the editor stays a one-file change.
31
31
  focusMode?: boolean;
32
32
  /** Typewriter scroll: hold the cursor line at vertical center while typing. Off by default. */
33
33
  typewriter?: boolean;
34
+ /** The surface posture. Prose is the writing instrument (72ch measure, larger type, looser
35
+ * leading); markup is the working surface (fills the card, denser). Prose by default. */
36
+ surface?: 'prose' | 'markup';
34
37
  }
35
38
 
36
39
  let {
@@ -43,6 +46,7 @@ through the adapter's render. Swapping the editor stays a one-file change.
43
46
  completionSources = [],
44
47
  focusMode = false,
45
48
  typewriter = false,
49
+ surface = 'prose',
46
50
  }: Props = $props();
47
51
 
48
52
  let host = $state<HTMLDivElement | null>(null);
@@ -57,6 +61,12 @@ through the adapter's render. Swapping the editor stays a one-file change.
57
61
  let modes: typeof import('./editor-modes.js') | null = null;
58
62
  let focusCompartment: import('@codemirror/state').Compartment | null = null;
59
63
  let typewriterCompartment: import('@codemirror/state').Compartment | null = null;
64
+ let surfaceCompartment: import('@codemirror/state').Compartment | null = null;
65
+ // The posture themes, swapped through the surface compartment. Each owns its type step and
66
+ // leading (the base theme deliberately sets neither on the content node, so the postures never
67
+ // contest it on adoption order). Built in onMount beside the base theme.
68
+ let proseTheme: import('@codemirror/state').Extension | null = null;
69
+ let markupTheme: import('@codemirror/state').Extension | null = null;
60
70
 
61
71
  onMount(async () => {
62
72
  const viewMod = await import('@codemirror/view');
@@ -75,9 +85,10 @@ through the adapter's render. Swapping the editor stays a one-file change.
75
85
  // tooltip above all) renders dark-on-dark instead of light-on-dark.
76
86
  const isDark = host.closest('[data-theme]')?.getAttribute('data-theme')?.includes('dark') ?? false;
77
87
  // The directive machinery treatment: rails, not bands. A row at depth N draws every rail
78
- // 1..N as literal nested brackets: 2px accent bars at x offsets 0-2, 4-6, and 8-10 with 2px
79
- // of surface between them, stacked as inset box shadows (top layer first, so each bar sits
80
- // over the spacer and deeper bar beneath it). The alphas step through the per-theme vars in
88
+ // 1..N as literal nested brackets: 2px accent bars at x offsets 0-2, 6-8, and 12-14 with 4px
89
+ // of surface between them (a gap of twice the bar weight, the floor for two parallel rules
90
+ // to read as separate lines rather than one thick one), stacked as inset box shadows (top
91
+ // layer first, so each bar sits over the spacer and deeper bar beneath it). The alphas step through the per-theme vars in
81
92
  // cairn-admin.css; the fallbacks are the light values, so the editor still renders sensibly
82
93
  // outside an admin theme wrapper. On a fence line the colon runs, brackets, and {attrs}
83
94
  // braces dim to the marker tone while the name and label keep a depth-stepped ink. Leaf and
@@ -91,7 +102,7 @@ through the adapter's render. Swapping the editor stays a one-file change.
91
102
  const rails = (depth: number, active = false): string => {
92
103
  const layers: string[] = [];
93
104
  for (let d = 1; d <= depth; d++) {
94
- const edge = 4 * d - 2;
105
+ const edge = 6 * d - 4;
95
106
  if (d > 1) layers.push(`inset ${edge - 2}px 0 0 0 var(--color-base-100, oklch(99% 0.004 75))`);
96
107
  const own = active && d === depth;
97
108
  layers.push(
@@ -120,19 +131,23 @@ through the adapter's render. Swapping the editor stays a one-file change.
120
131
  const theme = EditorView.theme(
121
132
  {
122
133
  '&': { backgroundColor: 'var(--color-base-100)', color: 'var(--color-base-content)', fontSize: '1rem' },
123
- // The 50vh floor keeps a short entry reading as a writing surface, and because the
124
- // contenteditable content area carries the height, a click in the empty space below the
125
- // text still lands in the editor and focuses it. The 70ch cap with auto margins holds
126
- // the manuscript to a readable measure, centered in whatever width the card gives it.
134
+ // The 60vh floor keeps the surface reading as the page's center stage even when the
135
+ // entry is short, and because the contenteditable content area carries the height, a
136
+ // click in the empty space below the text still lands in the editor and focuses it.
137
+ // No inner measure cap: the surface fills the card the way a code editor fills its
138
+ // pane, and the card's own width (the host caps it near 89ch of this face) is the one
139
+ // constraint. The surface carries tables, attributed directives, and long URLs, so the
140
+ // ceiling leans toward the code-editor end of the ergonomic band rather than the
141
+ // long-form ideal; paragraphs wrap comfortably below it.
127
142
  '.cm-content': {
128
143
  // The theme roots set --font-editor to the self-hosted iA Writer Mono; the inline
129
144
  // fallback keeps the surface monospace outside an admin theme wrapper.
130
145
  fontFamily: "var(--font-editor, ui-monospace, monospace)",
131
- padding: '0.875rem 1.25rem',
132
- lineHeight: '1.8',
133
- minHeight: '50vh',
134
- maxWidth: '70ch',
135
- margin: '0 auto',
146
+ // Vertical padding holds at least one line-height of the body (1.8 x 1rem), with a
147
+ // touch more below than above (the optical center sits high); the sides then read as
148
+ // gutters rather than letterboxing.
149
+ padding: '2rem 1.25rem 2.5rem',
150
+ minHeight: '60vh',
136
151
  },
137
152
  '.cm-cursor': { borderLeftColor: 'var(--color-primary)' },
138
153
  // A quiet always-on focus hairline. :focus-visible is no escape here: browsers treat a
@@ -145,9 +160,10 @@ through the adapter's render. Swapping the editor stays a one-file change.
145
160
  outlineOffset: '-1px',
146
161
  },
147
162
  '.cm-line': { padding: '0' },
148
- // The gutter: directive rows pad left so the text clears the deepest rail stack. It is
149
- // static structure (caret-independent), so caret movement shifts no layout.
150
- '.cm-cairn-directive-fence, .cm-cairn-directive-content': { paddingLeft: '1.25rem' },
163
+ // The gutter: directive rows pad left so the text clears the deepest rail stack (the
164
+ // depth-3 bar ends at 14px, the active one at 15px; 1.5rem keeps ~10px of air beyond
165
+ // it). Static structure (caret-independent), so caret movement shifts no layout.
166
+ '.cm-cairn-directive-fence, .cm-cairn-directive-content': { paddingLeft: '1.5rem' },
151
167
  ...railRules,
152
168
  '.cm-cairn-directive-mark': { color: 'var(--color-muted)' },
153
169
  '.cm-cairn-directive-label': { color: 'var(--color-accent)' },
@@ -174,13 +190,41 @@ through the adapter's render. Swapping the editor stays a one-file change.
174
190
  color: 'var(--cairn-focus-dim-ink, oklch(66% 0.01 75))',
175
191
  backgroundColor: 'transparent',
176
192
  },
193
+ // The rails dim with their text: the rail color-mix reads --cairn-directive-rail-N per
194
+ // element, so overriding the percentages on dimmed lines re-resolves every bar in place.
195
+ // Without this the directive block keeps full-strength bars and becomes the one
196
+ // chromatic object in the dimmed field. The active step needs the override too: focus
197
+ // mode's lit unit is the caret PARAGRAPH while the caret-block class spans the whole
198
+ // container, so a container holding a blank line has dimmed rows that still carry the
199
+ // active rail.
200
+ '.cm-cairn-focus-dim': {
201
+ '--cairn-directive-rail-1': 'var(--cairn-focus-dim-rail-1, 24%)',
202
+ '--cairn-directive-rail-2': 'var(--cairn-focus-dim-rail-2, 28%)',
203
+ '--cairn-directive-rail-3': 'var(--cairn-focus-dim-rail-3, 32%)',
204
+ '--cairn-directive-rail-active': 'var(--cairn-focus-dim-rail-active, 36%)',
205
+ },
206
+ },
207
+ { dark: isDark },
208
+ );
209
+
210
+ // The prose posture: the writing instrument. A 72ch measure centered in the card, one type
211
+ // step up, looser leading. Markup posture (the base theme) keeps the dense fill for tables,
212
+ // directives, and long URLs. Placed after the base theme in the extension list, so its keys
213
+ // win the spec-order ties.
214
+ proseTheme = EditorView.theme(
215
+ {
216
+ // Scoped to the content node (not the editor root) so the base theme's root font-size
217
+ // never contests it, and so the 72ch measure resolves against the prose type step.
218
+ '.cm-content': { fontSize: '1.0625rem', lineHeight: '1.9', maxWidth: '72ch', margin: '0 auto' },
177
219
  },
178
220
  { dark: isDark },
179
221
  );
222
+ markupTheme = EditorView.theme({ '.cm-content': { lineHeight: '1.8' } }, { dark: isDark });
180
223
 
181
224
  modes = modesMod;
182
225
  focusCompartment = new stateMod.Compartment();
183
226
  typewriterCompartment = new stateMod.Compartment();
227
+ surfaceCompartment = new stateMod.Compartment();
184
228
 
185
229
  view = new EditorView({
186
230
  parent: host,
@@ -205,6 +249,7 @@ through the adapter's render. Swapping the editor stays a one-file change.
205
249
  highlightMod.cairnDirectivePlugin(),
206
250
  EditorView.contentAttributes.of({ spellcheck: 'true', autocorrect: 'on', autocapitalize: 'sentences' }),
207
251
  theme,
252
+ surfaceCompartment.of(surface === 'prose' ? proseTheme : markupTheme),
208
253
  EditorView.updateListener.of((update) => {
209
254
  if (update.docChanged) value = update.state.doc.toString();
210
255
  }),
@@ -237,11 +282,13 @@ through the adapter's render. Swapping the editor stays a one-file change.
237
282
  $effect(() => {
238
283
  const focus = focusMode;
239
284
  const typing = typewriter;
240
- if (!mounted || !view || !modes || !focusCompartment || !typewriterCompartment) return;
285
+ const posture = surface;
286
+ if (!mounted || !view || !modes || !focusCompartment || !typewriterCompartment || !surfaceCompartment) return;
241
287
  view.dispatch({
242
288
  effects: [
243
289
  focusCompartment.reconfigure(focus ? modes.focusMode() : []),
244
290
  typewriterCompartment.reconfigure(typing ? modes.typewriterScroll() : []),
291
+ surfaceCompartment.reconfigure((posture === 'prose' ? proseTheme : markupTheme) ?? []),
245
292
  ],
246
293
  });
247
294
  });
@@ -19,6 +19,9 @@ interface Props {
19
19
  focusMode?: boolean;
20
20
  /** Typewriter scroll: hold the cursor line at vertical center while typing. Off by default. */
21
21
  typewriter?: boolean;
22
+ /** The surface posture. Prose is the writing instrument (72ch measure, larger type, looser
23
+ * leading); markup is the working surface (fills the card, denser). Prose by default. */
24
+ surface?: 'prose' | 'markup';
22
25
  }
23
26
  /**
24
27
  * The `MarkdownEditor` seam (spec §6, seam 5): a thin wrapper over CodeMirror 6 exposing a bindable
@@ -1,4 +1,4 @@
1
- @font-face{font-family:'Figtree Variable';font-style:normal;font-display:swap;font-weight:300 900;src:url('./fonts/figtree.woff2') format('woff2')}
1
+ @font-face{font-family:'IBM Plex Sans Variable';font-style:normal;font-display:swap;font-weight:100 700;src:url('./fonts/ibm-plex-sans.woff2') format('woff2')}
2
2
  @font-face{font-family:'Bricolage Grotesque Variable';font-style:normal;font-display:swap;font-weight:400 800;src:url('./fonts/bricolage-grotesque.woff2') format('woff2')}
3
3
  @font-face{font-family:'iA Writer Mono';font-style:normal;font-display:swap;font-weight:400;src:url('./fonts/ia-writer-mono-latin-400-normal.woff2') format('woff2')}
4
4
  @font-face{font-family:'iA Writer Mono';font-style:normal;font-display:swap;font-weight:700;src:url('./fonts/ia-writer-mono-latin-700-normal.woff2') format('woff2')}
@@ -180,6 +180,10 @@
180
180
  outline-offset: -1px;
181
181
  }
182
182
 
183
+ :where([data-theme="cairn-admin"], [data-theme="cairn-admin-dark"]) .cairn-doc-title-dim:not(:focus) {
184
+ color: var(--cairn-focus-dim-ink);
185
+ }
186
+
183
187
  :where([data-theme="cairn-admin"], [data-theme="cairn-admin-dark"]) .menu li > button:not(.btn) {
184
188
  font: inherit;
185
189
  color: inherit;
@@ -3278,6 +3282,10 @@
3278
3282
  margin-inline: calc(var(--spacing) * -4);
3279
3283
  }
3280
3284
 
3285
+ :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .mx-1 {
3286
+ margin-inline: calc(var(--spacing) * 1);
3287
+ }
3288
+
3281
3289
  :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .mx-auto {
3282
3290
  margin-inline: auto;
3283
3291
  }
@@ -3294,10 +3302,6 @@
3294
3302
  }
3295
3303
  }
3296
3304
 
3297
- :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .my-1 {
3298
- margin-block: calc(var(--spacing) * 1);
3299
- }
3300
-
3301
3305
  @layer daisyui.l1.l2.l3 {
3302
3306
  :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .breadcrumbs {
3303
3307
  max-width: 100%;
@@ -3938,6 +3942,10 @@
3938
3942
  max-height: 60vh;
3939
3943
  }
3940
3944
 
3945
+ :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .min-h-16 {
3946
+ min-height: calc(var(--spacing) * 16);
3947
+ }
3948
+
3941
3949
  :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .min-h-64 {
3942
3950
  min-height: calc(var(--spacing) * 64);
3943
3951
  }
@@ -4052,6 +4060,18 @@
4052
4060
  max-width: 30%;
4053
4061
  }
4054
4062
 
4063
+ :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .max-w-\[49rem\] {
4064
+ max-width: 49rem;
4065
+ }
4066
+
4067
+ :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .max-w-\[56rem\] {
4068
+ max-width: 56rem;
4069
+ }
4070
+
4071
+ :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .max-w-\[72ch\] {
4072
+ max-width: 72ch;
4073
+ }
4074
+
4055
4075
  :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .max-w-full {
4056
4076
  max-width: 100%;
4057
4077
  }
@@ -4246,6 +4266,10 @@
4246
4266
  gap: calc(var(--spacing) * 6);
4247
4267
  }
4248
4268
 
4269
+ :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .gap-10 {
4270
+ gap: calc(var(--spacing) * 10);
4271
+ }
4272
+
4249
4273
  :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) :where(.space-y-1 > :not(:last-child)) {
4250
4274
  --tw-space-y-reverse: 0;
4251
4275
  margin-block-start: calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));
@@ -4626,6 +4650,10 @@
4626
4650
  padding-inline: calc(var(--spacing) * 6);
4627
4651
  }
4628
4652
 
4653
+ :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .py-0 {
4654
+ padding-block: calc(var(--spacing) * 0);
4655
+ }
4656
+
4629
4657
  :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .py-1 {
4630
4658
  padding-block: calc(var(--spacing) * 1);
4631
4659
  }
@@ -4702,6 +4730,10 @@
4702
4730
  font-family: var(--font-display);
4703
4731
  }
4704
4732
 
4733
+ :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .font-\[family-name\:var\(--font-editor\,ui-monospace\,monospace\)\] {
4734
+ font-family: var(--font-editor, ui-monospace,monospace);
4735
+ }
4736
+
4705
4737
  :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .font-mono {
4706
4738
  font-family: var(--font-mono);
4707
4739
  }
@@ -4760,6 +4792,16 @@
4760
4792
  font-size: .8125rem;
4761
4793
  }
4762
4794
 
4795
+ :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .text-\[1\.0625rem\] {
4796
+ font-size: 1.0625rem;
4797
+ }
4798
+
4799
+ @layer daisyui.l1.l2 {
4800
+ :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .textarea-sm {
4801
+ font-size: max(var(--font-size, .75rem), .75rem);
4802
+ }
4803
+ }
4804
+
4763
4805
  :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .leading-relaxed {
4764
4806
  --tw-leading: var(--leading-relaxed);
4765
4807
  line-height: var(--leading-relaxed);
@@ -5379,6 +5421,12 @@
5379
5421
  }
5380
5422
  }
5381
5423
 
5424
+ @media (width >= 64rem) {
5425
+ :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .lg\:grid-cols-\[1fr_17rem\] {
5426
+ grid-template-columns: 1fr 17rem;
5427
+ }
5428
+ }
5429
+
5382
5430
  @media (width >= 64rem) {
5383
5431
  :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .lg\:grid-cols-\[1fr_20rem\] {
5384
5432
  grid-template-columns: 1fr 20rem;
@@ -5397,6 +5445,12 @@
5397
5445
  }
5398
5446
  }
5399
5447
 
5448
+ @media (width >= 64rem) {
5449
+ :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .lg\:gap-10 {
5450
+ gap: calc(var(--spacing) * 10);
5451
+ }
5452
+ }
5453
+
5400
5454
  @media (width >= 64rem) {
5401
5455
  :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .lg\:border-r {
5402
5456
  border-right-style: var(--tw-border-style);
@@ -5422,6 +5476,18 @@
5422
5476
  }
5423
5477
  }
5424
5478
 
5479
+ @media (width >= 64rem) {
5480
+ :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .lg\:px-10 {
5481
+ padding-inline: calc(var(--spacing) * 10);
5482
+ }
5483
+ }
5484
+
5485
+ @media (width >= 64rem) {
5486
+ :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .lg\:py-8 {
5487
+ padding-block: calc(var(--spacing) * 8);
5488
+ }
5489
+ }
5490
+
5425
5491
  @starting-style {
5426
5492
  :where([data-theme='cairn-admin'], [data-theme='cairn-admin-dark']) .starting\:-translate-y-2 {
5427
5493
  --tw-translate-y: calc(var(--spacing) * -2);
@@ -5438,7 +5504,7 @@
5438
5504
 
5439
5505
  [data-theme="cairn-admin"] {
5440
5506
  color-scheme: light;
5441
- --font-body: "Figtree Variable", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
5507
+ --font-body: "IBM Plex Sans Variable", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
5442
5508
  --font-display: "Bricolage Grotesque Variable", var(--font-body);
5443
5509
  --font-editor: "iA Writer Mono", ui-monospace, monospace;
5444
5510
  font-family: var(--font-body);
@@ -5463,6 +5529,10 @@
5463
5529
  --cairn-directive-ink-active: oklch(46% .16 300);
5464
5530
  --cairn-code-chip: oklch(94.5% .008 75);
5465
5531
  --cairn-focus-dim-ink: oklch(66% .01 75);
5532
+ --cairn-focus-dim-rail-1: 24%;
5533
+ --cairn-focus-dim-rail-2: 28%;
5534
+ --cairn-focus-dim-rail-3: 32%;
5535
+ --cairn-focus-dim-rail-active: 36%;
5466
5536
  --color-neutral: oklch(32% .012 75);
5467
5537
  --color-neutral-content: oklch(96% .004 75);
5468
5538
  --color-info: oklch(52% .12 240);
@@ -5489,7 +5559,7 @@
5489
5559
 
5490
5560
  [data-theme="cairn-admin-dark"] {
5491
5561
  color-scheme: dark;
5492
- --font-body: "Figtree Variable", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
5562
+ --font-body: "IBM Plex Sans Variable", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
5493
5563
  --font-display: "Bricolage Grotesque Variable", var(--font-body);
5494
5564
  --font-editor: "iA Writer Mono", ui-monospace, monospace;
5495
5565
  font-family: var(--font-body);
@@ -5514,6 +5584,10 @@
5514
5584
  --cairn-directive-ink-active: oklch(82% .14 300);
5515
5585
  --cairn-code-chip: oklch(29.5% .012 75);
5516
5586
  --cairn-focus-dim-ink: oklch(53% .01 75);
5587
+ --cairn-focus-dim-rail-1: 21%;
5588
+ --cairn-focus-dim-rail-2: 25%;
5589
+ --cairn-focus-dim-rail-3: 29%;
5590
+ --cairn-focus-dim-rail-active: 33%;
5517
5591
  --color-neutral: oklch(80% .01 75);
5518
5592
  --color-neutral-content: oklch(22% .008 75);
5519
5593
  --color-info: oklch(72% .12 240);
@@ -1,4 +1,4 @@
1
- Copyright 2022 The Figtree Project Authors (https://github.com/erikdkennedy/figtree)
1
+ Copyright 2019 IBM Corp. All rights reserved. IBMPlexSans-Italic[wdth,wght].ttf: Copyright 2019 IBM Corp. All rights reserved.
2
2
 
3
3
  This Font Software is licensed under the SIL Open Font License, Version 1.1.
4
4
  This license is copied below, and is also available with a FAQ at:
@@ -18,7 +18,7 @@ with others.
18
18
 
19
19
  The OFL allows the licensed fonts to be used, studied, modified and
20
20
  redistributed freely as long as they are not sold by themselves. The
21
- fonts, including any derivative works, can be bundled, embedded,
21
+ fonts, including any derivative works, can be bundled, embedded,
22
22
  redistributed and/or sold with any software provided that any reserved
23
23
  names are not used by derivative works. The fonts and derivatives,
24
24
  however, cannot be released under any other type of license. The
@@ -29,7 +29,7 @@ const SHARED_STYLE = `:root {
29
29
  --shadow: 0 1px 2px oklch(28% 0.02 75 / 0.05), 0 18px 40px -12px oklch(28% 0.02 75 / 0.16);
30
30
  --radius-box: 1rem;
31
31
  --radius-field: 0.625rem;
32
- --font: 'Figtree Variable', system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
32
+ --font: 'IBM Plex Sans Variable', system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
33
33
  }
34
34
  @media (prefers-color-scheme: dark) {
35
35
  :root {
@@ -104,7 +104,7 @@ main {
104
104
  h1 {
105
105
  margin: 0 0 0.75rem;
106
106
  font-size: 1.6rem;
107
- font-weight: 800;
107
+ font-weight: 700;
108
108
  letter-spacing: -0.02em;
109
109
  line-height: 1.15;
110
110
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glw907/cairn-cms",
3
- "version": "0.52.0",
3
+ "version": "0.53.0",
4
4
  "description": "Embedded, magic-link, GitHub-committing CMS for SvelteKit/Cloudflare sites.",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -244,8 +244,10 @@ identical on every host regardless of the site's own theme.
244
244
 
245
245
  <div class="drawer-content flex flex-col">
246
246
  <!-- The topbar is a flat, opaque continuation of the sidebar's brand band: same surface and the
247
- same hairline, no shadow, so the two form one clean header strip across the sidebar seam. -->
248
- <div class="navbar bg-base-100 border-b border-[var(--cairn-card-border)] sticky top-0 z-30 gap-2 px-4 lg:px-8">
247
+ same hairline, no shadow, so the two form one clean header strip across the sidebar seam.
248
+ The height is pinned to the brand band's h-16 (a content-driven navbar drifts with font
249
+ metrics, and the two border-bottoms stop meeting at the seam). -->
250
+ <div class="navbar bg-base-100 border-b border-[var(--cairn-card-border)] sticky top-0 z-30 h-16 min-h-16 gap-2 px-4 py-0 lg:px-8">
249
251
  <div class="flex-none lg:hidden">
250
252
  <label for="cairn-drawer" aria-label="Open menu" class="btn btn-square btn-ghost">
251
253
  <MenuIcon class="h-5 w-5" />
@@ -293,7 +295,7 @@ identical on every host regardless of the site's own theme.
293
295
  </div>
294
296
  </div>
295
297
 
296
- <main class="flex-1 p-4 lg:p-8">
298
+ <main class="flex-1 p-4 lg:px-10 lg:py-8">
297
299
  {@render children()}
298
300
  </main>
299
301
 
@@ -380,7 +382,7 @@ identical on every host regardless of the site's own theme.
380
382
 
381
383
  <div class="drawer-side">
382
384
  <label for="cairn-drawer" aria-label="Close menu" class="drawer-overlay"></label>
383
- <nav class="bg-base-100 flex min-h-full w-64 flex-col border-r border-[var(--cairn-card-border)]" aria-label="Site content">
385
+ <nav class="bg-base-100 flex min-h-full w-56 flex-col border-r border-[var(--cairn-card-border)]" aria-label="Site content">
384
386
  <!-- Brand band, the same height as the topbar. The mark sits in a filled "app-icon" tile, which
385
387
  anchors the corner as a deliberate brand object rather than a washed box. The logo and
386
388
  wordmark link to the admin home. -->