@erclx/canon 4.109.0 → 4.110.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/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/{canon-feedback-file → canon-feedback}/REQUIREMENT.md +7 -6
- package/claude/skills/{canon-feedback-file → canon-feedback}/SKILL.md +9 -19
- package/claude/skills/canon-feedback-triage/REQUIREMENT.md +1 -1
- package/claude/skills/canon-feedback-triage/SKILL.md +1 -1
- package/claude/skills/draft-wiki/REQUIREMENT.md +42 -0
- package/claude/skills/draft-wiki/SKILL.md +83 -0
- package/claude/skills/git-issue/REQUIREMENT.md +1 -1
- package/claude/skills/git-issue/SKILL.md +1 -1
- package/docs/agents/teach.md +4 -2
- package/docs/workflow/ai-workflow.md +2 -1
- package/package.json +1 -1
- package/src/claude/cases/authoring.ts +5 -0
- package/src/claude/cases/setup.ts +1 -1
- package/src/design/components.ts +382 -90
- package/src/teach/nav.ts +406 -56
package/src/design/components.ts
CHANGED
|
@@ -200,10 +200,20 @@ export const COMPONENTS: readonly Component[] = [
|
|
|
200
200
|
HAND_DRAWN_FIGURE,
|
|
201
201
|
]
|
|
202
202
|
|
|
203
|
+
/**
|
|
204
|
+
* Where the course sidebar stops being a column and becomes an overlay.
|
|
205
|
+
*
|
|
206
|
+
* The media query below and both `matchMedia` calls in `@/teach/nav` read this
|
|
207
|
+
* one value. Three literals is what it was, and each had a test asserting its
|
|
208
|
+
* own copy, so a half-finished move passed green while the scripts switched at
|
|
209
|
+
* one width and the layout at another.
|
|
210
|
+
*/
|
|
211
|
+
export const TEACH_SIDEBAR_BREAKPOINT = 1100
|
|
212
|
+
|
|
203
213
|
const TEACH_CHROME: Component = {
|
|
204
214
|
name: 'teach-chrome',
|
|
205
215
|
note: [
|
|
206
|
-
'The masthead, breadcrumb, jump menu, theme toggle,
|
|
216
|
+
'The masthead, breadcrumb, jump menu, theme toggle, course sidebar, and footer',
|
|
207
217
|
'navigation shared by every teach page. Recovered from two gitignored',
|
|
208
218
|
'course.css files that predate a regression that dropped this layer from the',
|
|
209
219
|
"generator, and rewritten onto this module's tokens rather than the",
|
|
@@ -230,6 +240,7 @@ const TEACH_CHROME: Component = {
|
|
|
230
240
|
'--t3',
|
|
231
241
|
'--t4',
|
|
232
242
|
'--t5',
|
|
243
|
+
'--t6',
|
|
233
244
|
],
|
|
234
245
|
rules: `:root {
|
|
235
246
|
--teach-sans: var(--type-body-family);
|
|
@@ -267,11 +278,16 @@ const TEACH_CHROME: Component = {
|
|
|
267
278
|
|
|
268
279
|
html { scroll-padding-top: calc(var(--teach-mast-h) + 1rem); }
|
|
269
280
|
|
|
270
|
-
/* No
|
|
271
|
-
|
|
281
|
+
/* No padding at all on the body, so the sticky bar reaches both window edges
|
|
282
|
+
and the sidebar can travel the full height. The gutter moves onto \`main\` and
|
|
283
|
+
the bottom room onto \`.pane\`: a flex item's sticky containing block is the
|
|
284
|
+
flex container's content box, so padding here is space the sidebar could
|
|
285
|
+
never reach and it would ride up by exactly that much near the foot. */
|
|
272
286
|
body {
|
|
273
287
|
margin: 0;
|
|
274
|
-
padding: 0
|
|
288
|
+
padding: 0;
|
|
289
|
+
display: flex;
|
|
290
|
+
align-items: flex-start;
|
|
275
291
|
background: var(--color-background);
|
|
276
292
|
color: var(--color-text-body);
|
|
277
293
|
font-family: var(--teach-sans);
|
|
@@ -287,6 +303,24 @@ body {
|
|
|
287
303
|
main { box-sizing: border-box; max-width: calc(var(--teach-measure) + 3rem); margin: 0 auto; padding: 2.25rem 1.5rem 0; }
|
|
288
304
|
.wide-body { max-width: calc(var(--teach-measure) + 3rem); }
|
|
289
305
|
|
|
306
|
+
/* Everything but the sidebar. \`flow-root\` rather than a bare block, because a
|
|
307
|
+
bottom margin collapsing out of here makes the document taller than the flex
|
|
308
|
+
container and a sticky sidebar can only travel inside its containing block,
|
|
309
|
+
so it would ride up by exactly the escaped margin on a tall window. The
|
|
310
|
+
border box is what keeps \`min-height: 100vh\` plus the bottom room from
|
|
311
|
+
scrolling every short page by that padding. */
|
|
312
|
+
.pane {
|
|
313
|
+
flex: 1 1 auto;
|
|
314
|
+
min-width: 0;
|
|
315
|
+
display: flow-root;
|
|
316
|
+
box-sizing: border-box;
|
|
317
|
+
min-height: 100vh;
|
|
318
|
+
padding-bottom: 7rem;
|
|
319
|
+
border-left: 1px solid var(--color-border);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
html.sb-shut .pane { border-left: 0; }
|
|
323
|
+
|
|
290
324
|
.bar {
|
|
291
325
|
position: sticky;
|
|
292
326
|
top: 0;
|
|
@@ -296,10 +330,27 @@ main { box-sizing: border-box; max-width: calc(var(--teach-measure) + 3rem); mar
|
|
|
296
330
|
padding: 0 1.5rem;
|
|
297
331
|
}
|
|
298
332
|
|
|
333
|
+
/* The reading bar rides the masthead's own bottom edge rather than arriving as
|
|
334
|
+
a second element. It reports position inside the lesson, which nothing
|
|
335
|
+
reported, where the retired track reported position in the course, which the
|
|
336
|
+
sidebar now reports. */
|
|
337
|
+
.bar::after {
|
|
338
|
+
content: "";
|
|
339
|
+
position: absolute;
|
|
340
|
+
left: 0;
|
|
341
|
+
bottom: -1px;
|
|
342
|
+
height: 2px;
|
|
343
|
+
width: var(--read, 0%);
|
|
344
|
+
background: var(--color-accent);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/* The height is the token rather than the sum of two paddings, so the sidebar's
|
|
348
|
+
own top row lands on the same seam the bar's bottom border draws. */
|
|
299
349
|
.mast {
|
|
300
350
|
max-width: var(--teach-chrome);
|
|
301
351
|
margin: 0 auto;
|
|
302
|
-
|
|
352
|
+
min-height: var(--teach-mast-h);
|
|
353
|
+
padding: 0;
|
|
303
354
|
display: flex;
|
|
304
355
|
justify-content: space-between;
|
|
305
356
|
align-items: center;
|
|
@@ -454,28 +505,225 @@ main { box-sizing: border-box; max-width: calc(var(--teach-measure) + 3rem); mar
|
|
|
454
505
|
:root:not([data-theme="light"]) .theme .moon { display: block; }
|
|
455
506
|
}
|
|
456
507
|
|
|
457
|
-
/* ----
|
|
508
|
+
/* ---- The course sidebar ---- */
|
|
458
509
|
|
|
459
|
-
/*
|
|
460
|
-
|
|
510
|
+
/* A segmented progress strip used to sit here, one \`flex: 1\` segment per
|
|
511
|
+
lesson, and it stopped working at the scale a reader most needs it: fifty
|
|
512
|
+
lessons rendered as a row of dots. The sidebar reports course position
|
|
513
|
+
instead, at any length, and the bar reports position inside the lesson. */
|
|
461
514
|
|
|
462
|
-
.
|
|
515
|
+
/* The toggle in the masthead, left of the breadcrumb. */
|
|
516
|
+
.sb-fold {
|
|
517
|
+
flex: none;
|
|
518
|
+
width: 1.75rem;
|
|
519
|
+
height: 1.75rem;
|
|
520
|
+
margin-right: 0.35rem;
|
|
521
|
+
font: inherit;
|
|
522
|
+
font-size: var(--t5);
|
|
523
|
+
line-height: 1;
|
|
524
|
+
color: var(--color-muted);
|
|
525
|
+
background: transparent;
|
|
526
|
+
border: 0;
|
|
527
|
+
border-radius: 6px;
|
|
528
|
+
cursor: pointer;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
.sb-fold:hover { color: var(--color-text); background: var(--color-surface); }
|
|
532
|
+
|
|
533
|
+
.sb {
|
|
534
|
+
flex: 0 0 var(--sb-w, 16rem);
|
|
535
|
+
width: var(--sb-w, 16rem);
|
|
536
|
+
position: sticky;
|
|
537
|
+
top: 0;
|
|
538
|
+
align-self: flex-start;
|
|
539
|
+
height: 100vh;
|
|
463
540
|
display: flex;
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
541
|
+
flex-direction: column;
|
|
542
|
+
overflow: visible;
|
|
543
|
+
background: transparent;
|
|
544
|
+
font-family: var(--teach-sans);
|
|
545
|
+
transition: flex-basis 0.14s, width 0.14s;
|
|
468
546
|
}
|
|
469
547
|
|
|
470
|
-
.
|
|
471
|
-
|
|
472
|
-
|
|
548
|
+
html.sb-shut .sb { flex-basis: 0; width: 0; overflow: hidden; }
|
|
549
|
+
|
|
550
|
+
.sb-top {
|
|
551
|
+
flex: none;
|
|
552
|
+
display: flex;
|
|
553
|
+
align-items: center;
|
|
554
|
+
height: var(--teach-mast-h);
|
|
555
|
+
padding: 0 0.95rem;
|
|
556
|
+
border-bottom: 1px solid var(--color-border);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
.sb-ws { position: relative; }
|
|
560
|
+
|
|
561
|
+
.sb-ws > summary {
|
|
562
|
+
list-style: none;
|
|
563
|
+
cursor: pointer;
|
|
564
|
+
display: inline-flex;
|
|
565
|
+
align-items: center;
|
|
566
|
+
gap: 0.35rem;
|
|
567
|
+
padding: 0.2rem 0.4rem;
|
|
568
|
+
border-radius: 6px;
|
|
569
|
+
font-size: var(--t4);
|
|
570
|
+
font-weight: 650;
|
|
571
|
+
color: var(--color-text);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
.sb-ws > summary::-webkit-details-marker { display: none; }
|
|
575
|
+
.sb-ws > summary:hover, .sb-ws[open] > summary { background: var(--color-surface); color: var(--color-text); }
|
|
576
|
+
.sb-ws .car { color: var(--color-muted); display: inline-flex; align-items: center; }
|
|
577
|
+
|
|
578
|
+
.sb-wl {
|
|
579
|
+
position: absolute;
|
|
580
|
+
top: calc(100% + 0.35rem);
|
|
581
|
+
left: 0;
|
|
582
|
+
z-index: 40;
|
|
583
|
+
min-width: 13rem;
|
|
584
|
+
background: var(--color-background);
|
|
585
|
+
border: 1px solid var(--color-border);
|
|
586
|
+
border-radius: 9px;
|
|
587
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.14);
|
|
588
|
+
padding: 0.25rem;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
:root[data-theme="dark"] .sb-wl { box-shadow: 0 8px 28px rgba(0, 0, 0, 0.5); }
|
|
592
|
+
|
|
593
|
+
/* The popover chrome comes from \`.sb-wl\`, so the list nested inside it drops
|
|
594
|
+
its own rather than drawing a second border inside the first. */
|
|
595
|
+
.sb-wl .jump-list {
|
|
596
|
+
position: static;
|
|
597
|
+
min-width: 0;
|
|
598
|
+
max-width: none;
|
|
599
|
+
border: 0;
|
|
600
|
+
box-shadow: none;
|
|
601
|
+
padding: 0;
|
|
602
|
+
background: transparent;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
.sb-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding-bottom: 1rem; }
|
|
606
|
+
|
|
607
|
+
.sb-meta {
|
|
608
|
+
padding: 1rem 1.25rem 0.55rem;
|
|
609
|
+
color: var(--color-muted);
|
|
610
|
+
font-size: var(--t6);
|
|
611
|
+
font-variant-numeric: tabular-nums;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
.sb-filter { padding: 0 0.95rem 0.6rem; }
|
|
615
|
+
|
|
616
|
+
.sb-filter input {
|
|
617
|
+
width: 100%;
|
|
618
|
+
box-sizing: border-box;
|
|
619
|
+
font: inherit;
|
|
620
|
+
font-size: var(--t6);
|
|
621
|
+
color: var(--color-text);
|
|
622
|
+
background: transparent;
|
|
623
|
+
border: 1px solid var(--color-border);
|
|
624
|
+
border-radius: 7px;
|
|
625
|
+
padding: 0.3rem 0.55rem;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
.sb-filter input:focus { outline: none; border-color: var(--color-accent); }
|
|
629
|
+
|
|
630
|
+
.sb-list { list-style: none; margin: 0; padding: 0 0.7rem; }
|
|
631
|
+
.sb-list li { margin: 0; }
|
|
632
|
+
.sb-list li.hide { display: none; }
|
|
633
|
+
|
|
634
|
+
.sb-l {
|
|
635
|
+
position: relative;
|
|
636
|
+
display: flex;
|
|
637
|
+
gap: 0.55rem;
|
|
638
|
+
align-items: baseline;
|
|
639
|
+
padding: 0.38rem 0.55rem;
|
|
640
|
+
border-radius: 6px;
|
|
641
|
+
text-decoration: none;
|
|
642
|
+
color: var(--color-text-body);
|
|
643
|
+
font-size: var(--t5);
|
|
644
|
+
line-height: 1.45;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
.sb-l:hover { background: var(--color-surface); color: var(--color-text); }
|
|
648
|
+
|
|
649
|
+
.sb-n {
|
|
650
|
+
flex: none;
|
|
651
|
+
font-family: var(--teach-mono);
|
|
652
|
+
font-size: var(--t6);
|
|
653
|
+
color: var(--color-muted);
|
|
654
|
+
font-variant-numeric: tabular-nums;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
.sb-on { color: var(--color-text); font-weight: 650; }
|
|
658
|
+
|
|
659
|
+
.sb-on::before {
|
|
660
|
+
content: "";
|
|
661
|
+
position: absolute;
|
|
662
|
+
left: -0.6rem;
|
|
663
|
+
top: 0.28rem;
|
|
664
|
+
bottom: 0.28rem;
|
|
665
|
+
width: 2px;
|
|
473
666
|
border-radius: 2px;
|
|
667
|
+
background: var(--color-accent);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
.sb-on .sb-n { color: var(--color-accent); }
|
|
671
|
+
|
|
672
|
+
.sb-empty { padding: 0.3rem 1.2rem; color: var(--color-muted); font-size: var(--t6); }
|
|
673
|
+
|
|
674
|
+
.sb-foot {
|
|
675
|
+
flex: none;
|
|
676
|
+
padding: 0.7rem 1.25rem;
|
|
677
|
+
border-top: 1px solid var(--color-border);
|
|
678
|
+
color: var(--color-muted);
|
|
679
|
+
font-size: var(--t6);
|
|
680
|
+
font-variant-numeric: tabular-nums;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/* The panel is resizable between a width that still fits a lesson title and one
|
|
684
|
+
that would start eating the reading measure. */
|
|
685
|
+
.sb-grip {
|
|
686
|
+
position: absolute;
|
|
687
|
+
top: 0;
|
|
688
|
+
bottom: 0;
|
|
689
|
+
right: -3px;
|
|
690
|
+
width: 7px;
|
|
691
|
+
z-index: 25;
|
|
692
|
+
padding: 0;
|
|
693
|
+
border: 0;
|
|
694
|
+
background: transparent;
|
|
695
|
+
cursor: col-resize;
|
|
696
|
+
touch-action: none;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
.sb-grip::before {
|
|
700
|
+
content: "";
|
|
701
|
+
position: absolute;
|
|
702
|
+
left: 3px;
|
|
703
|
+
top: 50%;
|
|
704
|
+
transform: translateY(-50%);
|
|
705
|
+
width: 1px;
|
|
706
|
+
height: 2.2rem;
|
|
707
|
+
border-radius: 1px;
|
|
474
708
|
background: var(--color-border);
|
|
709
|
+
opacity: 0;
|
|
710
|
+
transition: opacity 0.12s;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
.sb-grip:hover::before, .sb-grip:focus-visible::before, html.sb-drag .sb-grip::before {
|
|
714
|
+
opacity: 1;
|
|
715
|
+
background: var(--color-accent);
|
|
475
716
|
}
|
|
476
717
|
|
|
477
|
-
.
|
|
478
|
-
.
|
|
718
|
+
.sb-grip:focus-visible { outline: none; }
|
|
719
|
+
html.sb-drag { cursor: col-resize; user-select: none; }
|
|
720
|
+
html.sb-shut .sb-grip { display: none; }
|
|
721
|
+
|
|
722
|
+
/* Both belong to the overlay and the script appends both on every page, so
|
|
723
|
+
each is hidden until the breakpoint hands it a job. Without this the close
|
|
724
|
+
control renders as an unstyled button stretched across the column, and the
|
|
725
|
+
scrim becomes a flex item of the body. */
|
|
726
|
+
.sb-close, .sb-scrim { display: none; }
|
|
479
727
|
|
|
480
728
|
/* ---- Footer navigation, the only place it appears ---- */
|
|
481
729
|
|
|
@@ -556,8 +804,17 @@ main { box-sizing: border-box; max-width: calc(var(--teach-measure) + 3rem); mar
|
|
|
556
804
|
|
|
557
805
|
.jump-list a:hover { background: var(--color-surface); }
|
|
558
806
|
.jump-list .n { font-family: var(--teach-mono); font-size: 0.75rem; color: var(--color-muted); }
|
|
559
|
-
|
|
560
|
-
.
|
|
807
|
+
/* The third column used to hold a status dot that read the same on every row,
|
|
808
|
+
so it reported nothing. It is a per-entry trailing slot now: the workspace
|
|
809
|
+
menu puts a lesson count there and the lesson menu leaves it empty. */
|
|
810
|
+
.jump-list .ct {
|
|
811
|
+
font-family: var(--teach-mono);
|
|
812
|
+
font-size: var(--t6);
|
|
813
|
+
color: var(--color-muted);
|
|
814
|
+
font-variant-numeric: tabular-nums;
|
|
815
|
+
text-align: right;
|
|
816
|
+
}
|
|
817
|
+
|
|
561
818
|
.jump-list li.at a { background: var(--color-teach-accent-bg); }
|
|
562
819
|
.jump-list li.at .n { color: var(--color-accent); }
|
|
563
820
|
.jump-list li.soon a { color: var(--color-muted); }
|
|
@@ -606,6 +863,94 @@ main { box-sizing: border-box; max-width: calc(var(--teach-measure) + 3rem); mar
|
|
|
606
863
|
min-width: 0;
|
|
607
864
|
max-width: none;
|
|
608
865
|
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
/* ---- What gives way below 1100px ---- */
|
|
869
|
+
|
|
870
|
+
/* Hiding the sidebar outright leaves the toggle painted exactly where it cannot
|
|
871
|
+
act, and it deletes the filter and the per-lesson outline, which the
|
|
872
|
+
breadcrumb caret does not carry. The panel becomes an overlay instead, and it
|
|
873
|
+
states its own ground because \`.sb\` is transparent by design for a column
|
|
874
|
+
sitting on the page rather than floating over the lesson. */
|
|
875
|
+
@media (max-width: ${TEACH_SIDEBAR_BREAKPOINT}px) {
|
|
876
|
+
.pane { border-left: 0; }
|
|
877
|
+
|
|
878
|
+
.sb {
|
|
879
|
+
position: fixed;
|
|
880
|
+
top: 0;
|
|
881
|
+
left: 0;
|
|
882
|
+
bottom: 0;
|
|
883
|
+
z-index: 60;
|
|
884
|
+
width: min(21rem, 86vw);
|
|
885
|
+
height: 100dvh;
|
|
886
|
+
overflow-y: auto;
|
|
887
|
+
background: var(--color-background);
|
|
888
|
+
border-right: 1px solid var(--color-border);
|
|
889
|
+
box-shadow: 0 0 40px rgb(0 0 0 / 35%);
|
|
890
|
+
transform: translateX(-100%);
|
|
891
|
+
transition: transform 0.18s;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
html:not(.sb-shut) .sb { transform: none; }
|
|
895
|
+
|
|
896
|
+
/* The wide rule collapses a shut panel to \`width: 0\` and outranks a bare
|
|
897
|
+
\`.sb\` in here, which produced a one-pixel panel whose contents spilled out
|
|
898
|
+
and intercepted every click on the toggle. The shut state restates the
|
|
899
|
+
width so the transform is what moves the panel off screen. */
|
|
900
|
+
html.sb-shut .sb { width: min(21rem, 86vw); flex-basis: auto; overflow-y: auto; }
|
|
901
|
+
|
|
902
|
+
/* A transform moves the panel out of sight and leaves every control inside it
|
|
903
|
+
in the tab order, so the first Tab from a shut page landed on the workspace
|
|
904
|
+
switcher inside an invisible panel. Visibility takes it out of the
|
|
905
|
+
sequence, delayed on the way out so the slide still shows. */
|
|
906
|
+
html.sb-shut .sb { visibility: hidden; transition: transform 0.18s, visibility 0s linear 0.18s; }
|
|
907
|
+
html:not(.sb-shut) .sb { visibility: visible; transition: transform 0.18s, visibility 0s; }
|
|
908
|
+
|
|
909
|
+
.sb-grip { display: none; }
|
|
910
|
+
|
|
911
|
+
/* The panel covers the masthead and the toggle that opened it, so it carries
|
|
912
|
+
its own way out rather than leaving the scrim as the only route on a phone.
|
|
913
|
+
It carries no border, at 1.75rem to match \`.sb-fold\` and \`.theme\`, which also clears
|
|
914
|
+
the 24 pixel pointer target floor. */
|
|
915
|
+
.sb-close {
|
|
916
|
+
display: inline-flex;
|
|
917
|
+
align-items: center;
|
|
918
|
+
justify-content: center;
|
|
919
|
+
position: absolute;
|
|
920
|
+
top: 0.55rem;
|
|
921
|
+
right: 0.6rem;
|
|
922
|
+
z-index: 61;
|
|
923
|
+
appearance: none;
|
|
924
|
+
width: 1.75rem;
|
|
925
|
+
height: 1.75rem;
|
|
926
|
+
font: inherit;
|
|
927
|
+
font-size: var(--t5);
|
|
928
|
+
line-height: 1;
|
|
929
|
+
color: var(--color-muted);
|
|
930
|
+
background: transparent;
|
|
931
|
+
border: 0;
|
|
932
|
+
border-radius: 6px;
|
|
933
|
+
cursor: pointer;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
.sb-close:hover { color: var(--color-text); background: var(--color-surface); }
|
|
937
|
+
|
|
938
|
+
.sb-scrim {
|
|
939
|
+
display: block;
|
|
940
|
+
position: fixed;
|
|
941
|
+
inset: 0;
|
|
942
|
+
z-index: 55;
|
|
943
|
+
background: rgb(0 0 0 / 45%);
|
|
944
|
+
opacity: 0;
|
|
945
|
+
pointer-events: none;
|
|
946
|
+
transition: opacity 0.18s;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
html:not(.sb-shut) .sb-scrim { opacity: 1; pointer-events: auto; }
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
@media (prefers-reduced-motion: reduce) {
|
|
953
|
+
.sb, .sb-scrim, .sb-grip::before { transition: none; }
|
|
609
954
|
}`,
|
|
610
955
|
}
|
|
611
956
|
|
|
@@ -1148,83 +1493,30 @@ ol.succ li { margin-bottom: 0.4rem; }
|
|
|
1148
1493
|
const TEACH_OUTLINE: Component = {
|
|
1149
1494
|
name: 'teach-outline',
|
|
1150
1495
|
note: [
|
|
1151
|
-
'The in-lesson outline
|
|
1152
|
-
'
|
|
1496
|
+
'The in-lesson outline, folded under the lesson being read in the course',
|
|
1497
|
+
'sidebar. It used to be a fixed rail in the gutter the reading measure left',
|
|
1498
|
+
'over, which reported the same thing twice once the sidebar carried the',
|
|
1499
|
+
'course and disappeared entirely below 1420px.',
|
|
1153
1500
|
].join('\n '),
|
|
1154
|
-
reads: [
|
|
1155
|
-
|
|
1156
|
-
'--color-text-body',
|
|
1157
|
-
'--color-muted',
|
|
1158
|
-
'--color-accent',
|
|
1159
|
-
'--teach-sans',
|
|
1160
|
-
'--teach-measure',
|
|
1161
|
-
'--teach-mast-h',
|
|
1162
|
-
'--t5',
|
|
1163
|
-
],
|
|
1164
|
-
rules: `/* ---- In-lesson outline, in the gutter the measure leaves over ---- */
|
|
1165
|
-
|
|
1166
|
-
.outline {
|
|
1167
|
-
position: fixed;
|
|
1168
|
-
top: calc(var(--teach-mast-h) + 2.2rem);
|
|
1169
|
-
/* Four pixels of padding with the position pulled back by the same amount,
|
|
1170
|
-
so a focus ring at the rail's edge has somewhere to draw. \`overflow-y\`
|
|
1171
|
-
clips horizontally too, which took a bite out of every ring in here. */
|
|
1172
|
-
left: calc(50% + var(--teach-measure) / 2 + 2.5rem - 4px);
|
|
1173
|
-
padding: 0 4px;
|
|
1174
|
-
width: calc(13rem + 8px);
|
|
1175
|
-
max-height: calc(100vh - var(--teach-mast-h) - 4rem);
|
|
1176
|
-
overflow-y: auto;
|
|
1177
|
-
font-size: var(--t5);
|
|
1178
|
-
line-height: 1.45;
|
|
1179
|
-
}
|
|
1180
|
-
|
|
1181
|
-
/* The heading is a control rather than a label, because returning to the top
|
|
1182
|
-
of a long lesson is the one thing the rail could not do. */
|
|
1183
|
-
.outline .to-top {
|
|
1184
|
-
display: flex;
|
|
1185
|
-
align-items: center;
|
|
1186
|
-
gap: 0.4rem;
|
|
1187
|
-
width: 100%;
|
|
1188
|
-
font-family: var(--teach-sans);
|
|
1189
|
-
font-size: 0.6563rem;
|
|
1190
|
-
font-weight: 700;
|
|
1191
|
-
letter-spacing: 0.09em;
|
|
1192
|
-
text-transform: uppercase;
|
|
1193
|
-
color: var(--color-muted);
|
|
1194
|
-
background: none;
|
|
1195
|
-
border: 0;
|
|
1196
|
-
border-bottom: 1px solid var(--color-border);
|
|
1197
|
-
border-radius: 3px 3px 0 0;
|
|
1198
|
-
margin: 0 0 0.6rem;
|
|
1199
|
-
padding: 0.2rem 0 0.55rem;
|
|
1200
|
-
cursor: pointer;
|
|
1201
|
-
text-align: left;
|
|
1202
|
-
}
|
|
1501
|
+
reads: ['--color-text', '--color-text-secondary', '--color-accent', '--t6'],
|
|
1502
|
+
rules: `/* ---- In-lesson outline, folded under the lesson being read ---- */
|
|
1203
1503
|
|
|
1204
|
-
|
|
1504
|
+
/* The rail this replaces was \`position: fixed\` in the right-hand gutter, which
|
|
1505
|
+
only existed above 1420px and reported nothing at all below it. Folding the
|
|
1506
|
+
headings under the current lesson puts the course and the lesson in one
|
|
1507
|
+
column, so a reader tracks both in one place at any window width. */
|
|
1508
|
+
.sb-out { list-style: none; margin: 0.1rem 0 0.55rem; padding: 0 0 0 1.45rem; }
|
|
1205
1509
|
|
|
1206
|
-
.
|
|
1207
|
-
content: "";
|
|
1208
|
-
width: 11px;
|
|
1209
|
-
height: 8px;
|
|
1210
|
-
background:
|
|
1211
|
-
linear-gradient(var(--color-muted) 0 0) 0 0 / 11px 1.5px no-repeat,
|
|
1212
|
-
linear-gradient(var(--color-muted) 0 0) 0 3.2px / 8px 1.5px no-repeat,
|
|
1213
|
-
linear-gradient(var(--color-muted) 0 0) 0 6.5px / 11px 1.5px no-repeat;
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
.outline a {
|
|
1510
|
+
.sb-out a {
|
|
1217
1511
|
display: block;
|
|
1218
|
-
padding: 0.
|
|
1219
|
-
border-left: 2px solid var(--color-border);
|
|
1220
|
-
color: var(--color-muted);
|
|
1512
|
+
padding: 0.18rem 0.5rem;
|
|
1221
1513
|
text-decoration: none;
|
|
1514
|
+
color: var(--color-text-secondary);
|
|
1515
|
+
font-size: var(--t6);
|
|
1222
1516
|
}
|
|
1223
1517
|
|
|
1224
|
-
.
|
|
1225
|
-
.
|
|
1226
|
-
|
|
1227
|
-
@media (max-width: 1420px) { .outline { display: none; } }`,
|
|
1518
|
+
.sb-out a:hover { color: var(--color-text); }
|
|
1519
|
+
.sb-out a.on { color: var(--color-accent); }`,
|
|
1228
1520
|
}
|
|
1229
1521
|
|
|
1230
1522
|
const TEACH_REFERENCES: Component = {
|