Repository navigation
Expand file tree
/
Copy pathsurface-document.css
More file actions
1006 lines (950 loc) · 56.3 KB
/
Copy pathsurface-document.css
File metadata and controls
1006 lines (950 loc) · 56.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* =========================================================================
surface-document — the document register.
An OPT-IN VISUAL RULE under Foundations. It owns PRESENTATION ONLY: no
markup, no semantics, no template, and no generated preview. A consuming
surface writes its own markup and applies these classes to it, so two
semantic forms may share an appearance while sharing neither structure nor
behavior.
It serves BOTH lifecycles. A live surface references or vendors it and
re-syncs when it changes. An artifact scaffold may inline it at generation
time, after which the sealed copy does not re-sync. It loads no resource and
imports no stylesheet, so it inlines verbatim.
LOAD ORDER colors_and_type.css -> surface-document.css
A table of contents also needs surface-text-link.css, which its
entries compose for their interaction. An authorial callout, a
section framing or a section synthesis also needs
surface-panel.css and surface-treatments.css, and so does the
main + inspector composition; a locator the reader follows
needs surface-text-link.css. A page that loads
them all loads them as surface-document.html does:
colors_and_type.css -> surface-panel.css -> surface-text-link.css
-> surface-document.css -> surface-treatments.css.
It introduces no token and no palette value. Family, size, weight, leading,
tracking, foreground, space and rule color resolve through the foundation,
apart from the registered literals below.
ROLE OVER ELEMENT. Each text role is a complete text style — family, size,
weight, leading, tracking, foreground and margin — so the element carrying
it contributes nothing. An h2 carrying .doc-section-title is a section
title; an h4, h5, h6 or [role="heading"] carrying .doc-deep-title is the
same deep heading at every depth. Conforming a document role to the generic
heading metric of its element is a defect rather than a repair.
CATALOG
TEXT ROLE
meaning a complete text style selected by what the text is in the
document, never by its element, its size or its payload
population document title, section / subsection / deep headings,
lede, document body (in flow, in an authorial callout,
in a section framing or synthesis, and in a narrative
table's cells), entry title,
quotation text, operative label, metadata, inline code
(standing alone as a field value, the document locator),
table-of-contents entry, dense table cell; Caption
through the foundation's .caption
owner this module; the Caption role stays the foundation's
reuses foundation family, size, weight, leading, tracking and
foreground tokens
variation none per artifact; a new value enters with a named role
exclusions display type, the structural locator, panel labels,
compact actions and diagram chrome keep their own owners
example an h4 and an h6 carrying .doc-deep-title render alike
counterexample an uppercase mono label standing in for a content heading
THE LADDER. Document body is the foundation's Body step, the same 24 / 200
as a plain paragraph. Each heading role sits on one of the foundation's own
steps: the title on H1, the section on H2, the subsection on H3, and the
deep heading on Body itself, distinguished from body by weight (500 against
200) and by the space before it. No content heading is smaller than the
prose it governs. The ladder is checked, role by role, by tools/check-type-roles.mjs
(R7) and on rendered pages by tools/role-conformance.js.
TEXT RELATIONSHIP
meaning the space between one role and the next, carried by a
composition as a gap because every role sets margin 0
population a document flow, its sections, prose runs, titled
groups, groups, labeled values and action rows
owner this module
reuses foundation space tokens
variation a section nested inside a section leads by --space-4; an
item's action row may sit at the item's end (below)
exclusions page grid, column count, order and payload stay with the
consuming surface
example a heading and its body sit --space-3 apart wherever the
pair occurs
counterexample a hand-set margin on one subsection that its neighbors lack
ACTION ROW AT AN ITEM'S END
meaning in a repeated collection, each item's action row sits at
the item's end, so the items of one grid row present
their actions along one bottom edge however long their
copy is
population .doc-actions.doc-actions--end: the modifier on an item's
action row, in an item that is a flex column and
stretches to its grid row's height — a .surface-panel
item in a grid that keeps the default align-items, as the
key's repeated collection is
owner this module for the row's place in its item. The grid,
its columns, its rows and their heights, the order of
the items and each item's height stay with the consuming
surface; surface-action.css owns the controls and is not
involved
reuses an auto start margin inside the item's flex column: it
takes whatever block-axis space the item leaves. In grid
rows sized to their content that is only the space a
taller neighbor leaves, so the tallest item's row still
follows its copy by the item's gap; where the consumer
sizes rows otherwise, every item's row may move, and the
bottoms meet wherever each item is at least as tall as
its content
variation none
exclusions adopted by class only, never through a bare element or
through .doc-actions itself: a row without the modifier
keeps its place after its copy. Where the row has no free
block-axis space — in a block container, or in a column
no taller than its content — the modifier moves nothing;
in any flex or grid context that leaves it space, it
takes that space. No fixed heights, filler or per-item
offsets. The guarantee is a shared bottom edge, not a
shared top: when one row's controls wrap to more lines
than a neighbor's, its top sits higher by the extra lines
example two items side by side, one with a sentence of copy and
one with three, their actions on one line at the bottom
counterexample a min-height or a spacer that makes every item as tall as
the tallest; a margin tuned per item to line rows up
HIERARCHY RAIL
meaning "this belongs inside the level above" — indentation and
hierarchy; it organizes and does not emphasize
population nested sections to any depth, heading and content
together; structured text whose indentation is structure
owner this module; the rule and inset are the surface-shell
navigation panel's nested level, without that row's margin
reuses --space-4 inset, 1px --line-2 rule
variation depth only; inside a structured block its gap is zero
exclusions no wash, no blur, no shadow; spaces that only align
columns are not levels
example a level-7 heading nested one rail inside level 6, with the
same heading treatment
counterexample a smaller, lighter or recolored heading signaling depth
QUOTATION
meaning represented voice: words presented as someone's — quoted,
or introduced as what a speaker says — with optional
attribution. It records whose words these are and nothing
more: not verification, evidence, endorsement, agreement
or approval
population quoted statements in document text, long or short; an
earlier statement by the same author, quoted; speech the
prose introduces ("when another person says:"), including
illustrative speech; a quoted code excerpt, set as a
.doc-pre inside the .doc-quote
owner this module; who is speaking is the consuming surface's
editorial assignment, never inferred from the element, the
font or the presence of quotation marks
reuses the passage rail's geometry in the emphasis violet
(--ask-emphasis-violet), document body metrics, metadata
metrics for the attribution
variation none. .doc-quote--display is RETIRED (2026-09-18): it set
a short statement larger than the prose around it, while
the plain quotation set smaller. A quotation is body-sized
at every length. The neutral quotation rail is RETIRED
(2026-09-19): too faint to read as a boundary in either
theme, it made a set-apart passage look ghosted
exclusions not a container: no wash, no blur, no shadow. The
document's own thesis, question or contrast is not a
quotation even when it contains quoted words; it is an
authorial callout, or the section framing where it frames
a whole part
example a governing rule quoted with a footer naming its source
counterexample a quotation smaller or larger than the prose it sits in;
an authorial thesis set violet because it contains a
phrase in quotation marks
AUTHORIAL CALLOUT
meaning a passage the document states in its own voice and sets
apart from the reading flow: a thesis, a posed question,
an analytical contrast or a reasoning passage within the
argument. A thesis or question that frames a whole part
before its sections is the section framing, not a callout
population p.doc-body.surface-emphasis-rail for one paragraph; a
.doc-group.surface-emphasis-rail wrapping a lead-in and
the block it introduces, as one unit
owner surface-treatments.css owns the rail
(.surface-emphasis-rail, magenta); this module owns the
text role inside it, which stays document body
reuses the passage rail's geometry in the emphasis magenta;
document body metrics, authored inline emphasis included
variation none: an authorial callout is always magenta. The
rail's violet and cyan modifiers stay valid on any other
emphasis rail; in a document the violet rail is the
quotation's, so a callout never takes it. The rendered
check treats an emphasis rail on document text as a
callout
exclusions assigned by the passage's role, never by boldness: a bold
paragraph that labels the block after it, or an inline
strong run, is not a callout. Not a quotation, not a
container, never a heading. A preformatted block of the
document's own is already magenta and takes no second
class
example "Across the subject boundary, proof becomes evidence." —
the document's own compression of an argument, set apart
counterexample the same statement on a neutral rail, or as plain prose
after the reader has been told it is the point
SECTION SYNTHESIS
meaning the synthesis of the sections before it — "what the part
you just read comes to" — larger in scope than a callout,
so it is contained rather than railed
population a closing summary or compression of a document part
owner this module for the composition; surface-panel.css and
surface-treatments.css own the classes it composes
reuses .surface-separate + .surface-material-panel +
.surface-attach-free + .surface-elevation-flush +
.doc-group: the panel's tint and neutral perimeter, free
corners, no shadow. A .surface-emphasis-chip naming the
synthesis, with .surface-emphasis--magenta on the chip
itself so only its border takes the accent. The
synthesis text stays .doc-body at the Body step, authored
inline emphasis included
variation none: every synthesis in a document takes the same
composition, whatever the epistemic status of its text
exclusions no rail inside it, no .surface-emphasis on the panel (that
adds a bloom), no blur, no raised shadow, no hover, no
disclosure, never .surface-panel-support for its text.
The space before it is the consuming composition's; it
must read as a new unit after the last section, not as
a continuation of that section's closing passage. A
statement that frames the sections after it is the
section framing, not a synthesis
example a compression closing an argument: a flat tinted panel, a
magenta-bordered chip and the body-sized compression
counterexample one synthesis on a magenta rail and another as plain
prose; a synthesis in supporting-copy size inside a card
SECTION FRAMING
meaning the statement that frames a whole part before its
sections argue or ask it — the part's thesis, or the
question space it opens. It looks forward where a
synthesis looks back: it frames the sections after it and
concludes nothing
population the opening thesis or question of a document part, set
out after the part's introduction and before its first
section
owner this module for the composition; surface-panel.css and
surface-treatments.css own the classes it composes
reuses the section synthesis's composition, unchanged: the flat
panel, a .surface-emphasis-chip naming the framing with
.surface-emphasis--magenta on the chip, and .doc-body
text. Framing and synthesis share an appearance; the
chip's word and the panel's place say which one it is
variation none in presentation; the chip's word is the consuming
surface's (a thesis, a question space)
exclusions never a conclusion: not chipped or placed as a synthesis.
A thesis, question or contrast set apart inside
the argument stays an authorial callout on the magenta
rail. No rail inside it, no bloom, no blur, no raised
shadow, no hover, no disclosure. The space between the
introduction and the framing is the consuming
composition's; the two read as one opening
example a part that opens on its thesis: the introduction, then a
flat panel chipped "thesis", then the sections
counterexample the same opening chipped "conclusion"; the same opening
on the magenta callout rail among the sections
PREFORMATTED AND STRUCTURED TEXT
meaning a block whose line structure is the content
population diagrams, outlines and other passages whose line breaks
carry meaning; .doc-pre--structured where indentation is
structure
owner this module
reuses mono at the Small step, --lh-body, --fg-1; the passage
rail in the magenta emphasis color outside the block; the
hierarchy rail for structured levels inside it
variation .doc-pre--structured; .doc-pre-group for declared peer
groups; data-lead-lines 1, 2 or 3
exclusions not a container; never assigned by a blanket pre or code
selector; a block scrolls itself rather than widening its
parent
example lines beneath a label one rail in, a level beneath those
one rail further
counterexample leading spaces standing in for a level; a block with no
rail, whose lines read as loose text in the prose
PASSAGE RAIL
meaning "this passage is set apart from the reading flow and is
one unit" — the outer rail of a quotation, a
preformatted block and an authorial callout. The
GEOMETRY is shared; the COLOR says whose passage it is
population .doc-quote and .doc-pre, plain or structured, and
.surface-emphasis-rail composed onto document text
owner this module. Its geometry is surface-treatments'
.surface-emphasis-rail geometry — one 2px solid rule,
--space-4 inset — and an owner check (check-type-roles
R7) keeps the two equal. Color identity is NOT shared,
and every passage color is an emphasis accent:
quotation violet, --ask-emphasis-violet: represented voice,
declared as .surface-emphasis--violet declares it
preformatted the magenta emphasis rail, declared exactly as
.surface-emphasis-rail declares it: the line structure is
the content and is set apart as such
callout .surface-emphasis-rail itself, surface-treatments' own
magenta rail: the document's own voice, set apart
reuses 2px solid, --space-4 inset; --surface-emphasis-accent set
to --ask-emphasis-violet for a quotation and to
--ask-emphasis-magenta for a preformatted block
variation none: a quotation's rail is always violet, and a block's
and a callout's always magenta
exclusions a passage inside a railed passage (a .doc-quote, a
.doc-pre or a .surface-emphasis-rail inside another of
them) draws no rail of its own: the outer rail already
carries the passage, so one logical passage has one outer
rail and the outermost role names it — a quotation inside
an authorial callout is carried by the callout's magenta
rail. Two railed passages in a row are held apart by
--space-5, as two emphasis rails are. NEUTRAL IS
STRUCTURE ONLY: the 1px hierarchy rail structures a section,
a list or a block, and is never the outer boundary of a
set-apart passage. No wash, no
blur, no shadow. The emphasis violet here is a document
role; it names no review, proof or evidence state, and a
surface's own labeled uses of the accents keep their own
meanings
example a structured block with the magenta rail outside and a
neutral hierarchy rail for each level inside; a quotation
beside it on the same geometry in violet; a posed question
of the document's own on the magenta rail
counterexample a neutral outer rail on a standalone passage, which reads
as a ghost of a boundary; a quotation in magenta, or an
authorial statement in violet; two rails on one passage
TABLE OF CONTENTS
meaning structural navigation: where each part of this document
starts
population .doc-toc-list for the list, a nested .doc-toc-list that is
also a .doc-hierarchy for each level beneath it, and
.doc-toc-link for each entry
owner this module for the entry's type and the list's rhythm;
surface-text-link.css for the entry's interaction, composed
as class="doc-toc-link surface-text-link"
reuses the surface-shell navigation row's text metrics (mono,
Small, 300, tight leading, tight tracking, --fg-1) without
its padding, fill, radius or focus ring; the hierarchy
rail for nested levels
variation depth only
exclusions a table-of-contents entry is never document body and never
uppercase; its interaction is never restated here
example a two-level contents list inside a disclosure, each entry
with the partial magenta underline at rest
counterexample a bare anchor inside a body-text list item
DENSE TABLE
meaning a table whose values are tabular, technical payload — a
review matrix, a status ledger — declared as such. Being a
table does not make a table dense
population table.doc-dense-table, a population marker the consumer
sets. Its td carry .doc-table-cell and its th .doc-label;
.doc-table-cell appears nowhere else
owner this module for the cell's type only; the marker draws
nothing. Table geometry — rules, padding, the stacked
narrow-width layout — stays with the consuming surface
unless the table adopts the document table composition,
and a table without the marker (a narrative comparison, a
prose table) is not this role; it takes the narrative
table assignment
reuses mono at the Small step, 300, --lh-body, --fg-1, tabular
numerals
variation none
exclusions a dense table's cell never carries the document body role;
the marker is never implied by the td or th element; the
marker never implies the document table composition. It
sets technical type and is not a compact layout
example a status, a date and a digest in one row, all mono
counterexample every table on a page set mono because it is a table; a
dense table whose cells are set as body prose
NARRATIVE TABLE
meaning a table whose cells are prose — a comparison, a list of
dispositions — rather than tabular payload, set in the
register without being declared dense
population a table without table.doc-dense-table. Its th carry
.doc-label and its td .doc-body
owner this module for the type only; the assignment draws
nothing. Table geometry stays with the consuming surface
unless the table adopts the document table composition
reuses the operative label and document body roles
variation none
exclusions a narrative cell never carries .doc-table-cell; being in a
table never makes prose mono
example catalog dispositions: an item, a disposition and a
sentence of detail in each row
counterexample a prose table set mono because it is a table
DOCUMENT TABLE
meaning a shared table geometry a consumer adopts by class: row
rules, spacing, numeral alignment, line breaking, and a
table that scrolls sideways in its own box rather than
widening the page
population table.doc-table as the direct child of a .doc-table-scroll
box, for a narrative or a dense table alike; both markers
are needed. .doc-table-num on the th and td of a numeric
column whose values share a format
owner this module for the geometry of a table that adopts it.
The consumer owns the content, the columns, which tables
are dense, which columns are numeric, any per-table
widths, a name for each scroll box (role="region" with a
label), and whether a table adopts the composition at all
reuses the --line-2 rule under every row and the --line-1 rule
under the header row; --space-3 block padding and
--space-5 between columns; tabular figures; the overflow
cue
variation a narrative table keeps a 32rem measure and scrolls below
it; a dense value does not wrap; code in a cell breaks
only at its own break opportunities
exclusions never implied: not by table.doc-dense-table, not by a text
role on a cell and not by the table element, so a table
without both markers keeps its consumer's geometry, and a
table nested inside a cell takes none of the cell rules. It is
not a compact review density, and it has no stacked
narrow-width layout: a surface that must not scroll a
table sideways, or that stacks its rows, keeps its own
geometry and does not adopt it. Text keeps its role: the
composition sets no family, size, weight, leading,
tracking or foreground
example a dense table with an end-aligned count column, its header
row on the --line-1 rule, scrolling in its box at a phone
width with the cue beneath it
counterexample every table on a page given the composition because it is
a table; a compact layout made by overriding this padding
ENTRY TITLE
meaning the primary text of an operable entry in a result list or
record index: the name or the statement of the one record
the entry opens. It may be a short name or a full
statement, and it may wrap
population .doc-entry-title on the entry's primary text; the entry's
identifier and supporting fields take .doc-meta
owner this module for the text's type only. The entry element,
its interaction and its row geometry stay with the
consuming surface, as a dense table's geometry does
reuses Inter at the Body step, --fw-light (300), --lh-body,
--tracking-normal, --fg-1
variation none
exclusions an entry title is never a heading: it governs nothing
beneath it and sits outside the ladder. It is never
document body, never the panel's single-line primary
label, never an uppercase operative label, and it never
restates the entry's interaction
example a search result: its identifier in .doc-meta above a
finding's full statement wrapping across three lines
counterexample a result's primary text set as document body; a wrapping
statement set on the panel label's tight leading and
tracking
DOCUMENT LOCATOR
meaning a source address, DOI or path standing as a field value:
a literal string the reader copies or follows
population a code.doc-code that is the value of a .doc-labeled field
and stands inside no other text role; an address the
reader follows is an a.surface-text-link inside it
owner this module. It is an assignment of the inline-code role,
as the narrative table is of the label and body roles,
with no class, metric or foreground of its own
reuses .doc-code standing alone: mono at the Caption step, 300,
--fg-2, breaking anywhere, so a long address wraps inside
its field rather than widening it; surface-text-link's
interaction, which sets no foreground
variation none
exclusions .doc-code declares no leading, tracking or case, so a
locator keeps its literal form only where its container
sets none: as a field value. Inside a label it would take
the label's tracking and a smaller size, 0.9em of the
label's (the register keeps code's source case there);
inside a caption or a disclosure trigger, their capitals
and tracking; inside a panel title, its tracking. Apart
from that one case rule, the role enforces none of this
in any ancestry; the placement does. An address inside
running prose stays a body link. An identifier that names
a record stays .doc-meta, as an entry's does. Which tokens
link is the consumer's rule. An address in a page
header's chrome is outside this assignment
example a record's source link: a 191-character address wrapping
across several lines of its field at a phone width, in
its source case, and opening its source
counterexample an address set in an operative label's capitals and
tracking; a source address field set as document body
MAIN + INSPECTOR
meaning records in a main region beside an inspector that shows
the one selected: two regions of one document, read
together
population a repeated collection or record index in the main
region; one view per record in the inspector, a
.doc-titled heading over its fields in .doc-group and
.doc-labeled compositions
owner this module for the text roles and relationships in both
regions; surface-panel.css and surface-treatments.css own
the classes composed. Everything that makes the second
region an inspector is the consuming surface's
reuses one set of text roles across both regions: a role keeps
its treatment wherever it occurs, and the inspector's
prose stays .doc-body at the Body step. A record's name
is an entry title in the main region and the view's
heading in the inspector — a difference of role, since
the heading governs the fields beneath it, not of space.
The inspector is a separate surface chosen on the three
axes: .surface-separate + .surface-material-panel +
.surface-attach-free + .surface-elevation-flush, the flat
panel the section synthesis composes
variation none shared
exclusions no selector, layout or controller. The regions' grid,
proportions and breakpoint; the inspector's sticky place
and independent scroll; the regions' order at a narrow
width; selection, the current-item indication, focus
handling and the route back all stay with the consuming
surface. Nothing shrinks for the narrower region.
surface-action.css's magenta is attention only and never
marks the current item. The register's compositions and
.surface-action set display, so a consumer that hides a
view or a control with the hidden attribute supplies its
own guard
example the key's records-and-inspector section: four public
records and an inspector for the selected one, driven by
a page-local script
counterexample inspector prose set a step smaller than the records'; a
selected item marked by the magenta action edge
OVERFLOW CUE
meaning the block is wider than its box, and which side holds
hidden content
population a .doc-pre, or a document table's .doc-table-scroll box,
whose data-overflow attributes are set
owner this module draws it; surface-document-overflow.js, an
optional helper, reports the state
reuses the operative label metrics, --space-2, --space-6
variation "scroll >>", "<< scroll >>", "<< scroll"
exclusions nothing is shrunk, wrapped, enclosed or reflowed; a block
that fits shows nothing
example a wide diagram at a narrow width: a right-edge fade and
"scroll >>" held at the box's left edge
counterexample shrinking a diagram's type until it fits
PEER GROUPS IN STRUCTURED TEXT
meaning a grouped diagram: peer groups, each a label with the
lines beneath it, where the reader must see where one
group ends and the next begins
population .doc-pre-group, one per peer group, as the direct
children of a .doc-pre--structured block. A group opens
on its label, a .doc-pre-part; the lines beneath follow
in parts and hierarchy rails. A block that declares groups
holds nothing else at its top level
owner this module owns the rhythm; the consuming surface
declares the groups, and how it finds them in its own
source is its adapter, not a shared rule
reuses the block's one-line slot, 1lh: exactly one line between
successive groups, whatever their number. Inside a group
nothing is set apart: the label sits on its lines and the
lines on each other. A blank-line record on a group's
first line adds nothing: the one line between groups
stands for it, and before the first group it is dropped
variation none: every boundary between peer groups is one line, for
two groups or ten
exclusions groups are declared: no rule here and no check reads a
group from blank lines, capitals, font or line count, and
an adapter never finds one merely from capitals, font or
line count; no group-count threshold; no foundation token; not for verbatim code or
a quoted payload, whose blank lines are content; ordinary
.doc-group prose, dense tables and drawn diagrams keep
their own spacing. The hierarchy rails and the block's
outer rail are unchanged
example three groups, each a label with a line beneath it, one
line apart
counterexample a blank line between a label and its lines; two lines
between two groups; a separator the source happened to
type standing in for a declared group
REGISTERED LITERALS — no token expresses these values
1.4 metadata and quotation-attribution leading
1.3 Caption leading inside document compositions
1px, 2px hierarchy rail and passage rail widths
#000, transparent overflow mask stops: alpha only, never paint
0.9em inline code, the foundation code rule's own size
1lh, 2lh, 3lh blank source lines in structured text; 1lh is also
the one line between declared peer groups
32rem a narrative document table's minimum measure
LIMITS
- The blank-line slots and the peer-group line use the lh unit. A browser
without it drops those margins; the lines themselves are unchanged.
- data-lead-lines records one to three blank lines. No rule reads a
larger value.
- Without the helper a block or a table's box still scrolls and shows no
cue. Keyboard scrolling of either is not claimed: nothing here sets a
tabindex.
========================================================================= */
/* ---- Text roles ------------------------------------------------------- */
.doc-title {
font-family: var(--font-sans);
font-size: var(--fs-h1);
font-weight: var(--fw-regular);
line-height: var(--lh-heading);
letter-spacing: var(--tracking-tight);
color: var(--fg-1);
margin: 0;
}
.doc-section-title {
font-family: var(--font-sans);
font-size: var(--fs-h2);
font-weight: var(--fw-regular);
line-height: var(--lh-heading);
letter-spacing: var(--tracking-tight);
color: var(--fg-1);
margin: 0;
}
.doc-subsection-title {
font-family: var(--font-sans);
font-size: var(--fs-h3);
font-weight: var(--fw-light);
line-height: var(--lh-heading);
letter-spacing: var(--tracking-tight);
color: var(--fg-1);
margin: 0;
}
/* The title, section and subsection roles sit on the foundation's H1, H2 and
H3 steps. The deep heading — h4 and every deeper level: h5, h6, and past h6
through role="heading" aria-level — shares the Body step with body copy and
stays distinct by weight (500 against 200) and by the section space before
it. It never changes with depth — no smaller size, no lighter weight, no
foreground step. Depth is the hierarchy rail's. */
.doc-deep-title {
font-family: var(--font-sans);
font-size: var(--fs-body);
font-weight: var(--fw-medium);
line-height: var(--lh-heading);
letter-spacing: var(--tracking-normal);
color: var(--fg-1);
margin: 0;
}
/* Body copy is primary reading text: the foundation's Body step and --fg-1, the
same metric as a plain paragraph. Every heading above it is at least this
size. The lede shares the metric; composition, not size, makes it the lede. */
.doc-body {
font-family: var(--font-sans);
font-size: var(--fs-body);
font-weight: var(--fw-extralight);
line-height: var(--lh-body);
letter-spacing: var(--tracking-normal);
color: var(--fg-1);
margin: 0;
}
.doc-lede {
font-family: var(--font-sans);
font-size: var(--fs-body);
font-weight: var(--fw-extralight);
line-height: var(--lh-body);
letter-spacing: var(--tracking-normal);
color: var(--fg-1);
margin: 0;
}
/* The entry title is the primary text of an operable entry in a result list or
record index. It is neither a heading nor body. It takes Body's size, leading
and tracking, because it may wrap as a full statement, one weight step above
the body prose around the list. The entry element, its interaction and its
geometry stay the consumer's. */
.doc-entry-title {
font-family: var(--font-sans);
font-size: var(--fs-body);
font-weight: var(--fw-light);
line-height: var(--lh-body);
letter-spacing: var(--tracking-normal);
color: var(--fg-1);
margin: 0;
}
/* The operative register. The label is uppercase mono, for labels and status,
and is never a content heading. Metadata shares its family, size, weight and
tracking without the uppercase, on a looser leading. */
.doc-label {
font-family: var(--font-mono);
font-size: var(--fs-caption);
font-weight: var(--fw-light);
line-height: var(--lh-tight);
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
color: var(--fg-3);
margin: 0;
}
.doc-meta {
font-family: var(--font-mono);
font-size: var(--fs-caption);
font-weight: var(--fw-light);
line-height: 1.4;
letter-spacing: var(--tracking-wide);
color: var(--fg-3);
margin: 0;
}
/* Inside a sized document role, code is 0.9x its parent's computed font size.
Ordinary inline wrappers preserve that role's size. With no sized-role
ancestor, code falls back to the Caption step rather than the browser default.
Standing alone as a field value, it is the document locator (catalog). */
.doc-code {
font-family: var(--font-mono);
font-size: 0.9em;
font-weight: var(--fw-light);
color: var(--fg-2);
}
.doc-code:where(:not(:is(.doc-body, .doc-meta, .doc-label, .doc-deep-title, .doc-subsection-title, .doc-section-title, .doc-lede, .doc-title, .doc-quote, .doc-toc-link, .doc-table-cell, .doc-entry-title) *)) {
font-size: var(--fs-caption);
}
/* Code keeps its source case inside an operative label. The capitals are the
label's, and a literal set in them is a different literal: default-ASK is
not DEFAULT-ASK. Only the case changes; the code keeps its family, size,
weight and foreground, and plain text in a label keeps the capitals. */
.doc-label :is(code, .doc-code) { text-transform: none; }
/* Inline emphasis inside document text. Weight carries it: 500 against body
copy at 200, rather than the browser's bolder step. Scoped to the register,
so strong and em elsewhere keep their own styling. */
:where(.doc-flow, .doc-title, .doc-section-title, .doc-subsection-title, .doc-deep-title, .doc-lede, .doc-body, .doc-label, .doc-meta, .doc-quote, .doc-pre, .doc-table-cell, .doc-toc-link, .doc-entry-title) strong {
font-weight: var(--fw-medium);
color: var(--fg-1);
}
:where(.doc-flow, .doc-title, .doc-section-title, .doc-subsection-title, .doc-deep-title, .doc-lede, .doc-body, .doc-label, .doc-meta, .doc-quote, .doc-pre, .doc-table-cell, .doc-toc-link, .doc-entry-title) em {
font-style: italic;
}
/* Caption inside a document composition. The foundation's .caption owns the
role's size, weight, tracking, case and foreground; this adds the family,
the leading and the margin a composition relies on. */
:where(.doc-flow, .doc-section, .doc-prose, .doc-titled, .doc-group, .doc-labeled, .doc-hierarchy) > .caption {
font-family: var(--font-sans);
line-height: 1.3;
margin: 0;
}
/* Long unbroken tokens — digests, repository paths, URLs — never force a
horizontal scroll. */
.doc-code, .doc-meta, .doc-body a { overflow-wrap: anywhere; }
/* ---- Compositions: the space between roles ---------------------------- */
.doc-flow { display: flex; flex-direction: column; gap: var(--space-8); }
.doc-section { display: flex; flex-direction: column; gap: var(--space-3); }
.doc-prose { display: flex; flex-direction: column; gap: var(--space-4); }
.doc-titled { display: flex; flex-direction: column; gap: var(--space-3); }
.doc-group { display: flex; flex-direction: column; gap: var(--space-3); }
.doc-labeled { display: flex; flex-direction: column; gap: var(--space-2); }
/* A section nested inside a section. The parent's gap is the heading-to-body
rhythm, too tight to read a new subsection as starting, so the nesting adds
one shared lead wherever it occurs — nested rail levels included. */
.doc-section > .doc-section { margin-top: var(--space-4); }
.doc-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; }
/* An item's action row at the item's end, adopted by class on the row. In an
item that is a flex column and stretches to its grid row, the auto margin
takes the block-axis space the item leaves — in rows sized to their content,
the space a taller neighbor leaves. With no free space it moves nothing. The
guarantee is a shared bottom edge, not a shared top. */
.doc-actions--end { margin-block-start: auto; }
/* ---- Table of contents ------------------------------------------------ */
/* Structural navigation. The entry takes the surface-shell navigation row's
text metrics — mono, Small, 300, tight leading and tracking — without the
row's padding, fill, radius or focus ring, and composes surface-text-link
for its interaction: class="doc-toc-link surface-text-link" — the partial
magenta underline at rest, the full magenta underline on hover and the
module's own keyboard-focus underline, all owned by surface-text-link.css
and checked in each state on rendered pages. The list
carries the same family, size and leading, so each entry's line box is the
tight one and a wrapped entry keeps it; the anchor stays inline so its
underline follows every line. A nested list that is also a .doc-hierarchy is
one level down; this rule sits before the hierarchy rail in source order, so
the rail's inset wins where both apply. */
.doc-toc-list {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: var(--space-2);
font-family: var(--font-mono);
font-size: var(--fs-small);
line-height: var(--lh-tight);
}
.doc-toc-list .doc-toc-list { margin-top: var(--space-2); }
.doc-toc-link {
font-family: var(--font-mono);
font-size: var(--fs-small);
font-weight: var(--fw-light);
line-height: var(--lh-tight);
letter-spacing: var(--tracking-tight);
color: var(--fg-1);
margin: 0;
}
/* ---- Dense table ------------------------------------------------------ */
/* A value in a dense table is tabular payload: mono at the Small step, with
tabular numerals. The population is declared, never inferred from the
element: the consumer marks the table table.doc-dense-table, puts
.doc-table-cell on its td and .doc-label on its th. The marker has no rule
here because it draws nothing; table geometry stays with the consuming
surface unless the table adopts the document table composition below, and
an unmarked table is not this role. */
.doc-table-cell {
font-family: var(--font-mono);
font-size: var(--fs-small);
font-weight: var(--fw-light);
line-height: var(--lh-body);
letter-spacing: var(--tracking-normal);
color: var(--fg-1);
font-variant-numeric: tabular-nums;
margin: 0;
}
/* ---- Document table --------------------------------------------------- */
/* A composition a consumer adopts by class: table.doc-table as the direct child
of a .doc-table-scroll box. Both markers are needed; nothing implies either —
not the dense marker, not a text role on a cell, not the table element — so a
table without them and a table marked without its box keep their consumer's
geometry, and a table nested inside a cell takes none of the cell rules. The
box's own scroll, scroll padding and cue key on the box alone. It sets
geometry, line breaking and a numeric column's figures; text keeps its role. Every rule that reaches a
cell sits at zero specificity, except the code line-breaking rule, which must
outrank the register's own.
The box scrolls, so the page never does: a table wider than its column
scrolls inside it, and the header row stays in place and visible, so its
words, code and links are never hidden, clipped or repeated. scroll-padding
lands a control scrolled into view by the keyboard clear of the fade. */
:where(.doc-table-scroll) { overflow-x: auto; scroll-padding-inline: var(--space-6); }
:where(.doc-table-scroll > table.doc-table) { width: 100%; border-collapse: collapse; }
/* The fainter rule (--line-2) under every row, and the hairline divider
(--line-1) under the header row, so the header reads as the table's head.
Header cells sit on the header rule. */
:where(.doc-table-scroll > table.doc-table > * > tr, .doc-table-scroll > table.doc-table > tr) > :where(th, td) {
text-align: start;
vertical-align: top;
padding: var(--space-3) var(--space-5) var(--space-3) 0;
border-bottom: 1px solid var(--line-2);
}
:where(.doc-table-scroll > table.doc-table > * > tr, .doc-table-scroll > table.doc-table > tr) > :where(th:last-child, td:last-child) { padding-inline-end: 0; }
:where(.doc-table-scroll > table.doc-table > thead > tr) > :where(th) { vertical-align: bottom; border-bottom-color: var(--line-1); }
/* A narrative table keeps a readable measure: it never narrows below 32rem, and
inside that width its columns share space by their content. At a narrower
width it scrolls in its box rather than squeezing prose. */
:where(.doc-table-scroll > table.doc-table:not(.doc-dense-table)) { min-width: 32rem; }
/* A numeric column — its header and its cells — aligns to the end on tabular
figures, where its values share a format. */
:where(.doc-table-scroll > table.doc-table > * > tr, .doc-table-scroll > table.doc-table > tr) > :where(.doc-table-num) {
text-align: end;
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
/* A dense value keeps its line; a long one widens the table, which scrolls. */
:where(.doc-table-scroll > table.doc-table > * > tr, .doc-table-scroll > table.doc-table > tr) > :where(td.doc-table-cell) { white-space: nowrap; }
/* Code breaks only at its own opportunities — a hyphen, a slash, a space —
never between two letters. The register lets code break anywhere so that a
long token never forces a horizontal scroll; this table already scrolls in
its box. Line breaking only: the code's type is unchanged. */
.doc-table-scroll > table.doc-table :is(code, .doc-code) { overflow-wrap: normal; }
/* ---- Hierarchy rail --------------------------------------------------- */
/* The internal rhythm is a zero-specificity default, so a composition class
placed alongside (.doc-section, .doc-group, .doc-prose) sets its own gap
whatever the source order. */
:where(.doc-hierarchy) { display: flex; flex-direction: column; gap: var(--space-3); }
.doc-hierarchy {
padding-left: var(--space-4);
border-left: 1px solid var(--line-2);
}
/* ---- Passage rail ----------------------------------------------------- */
/* A quotation, a preformatted block and an authorial callout are set apart from
the reading flow, each as one unit, on the geometry surface-treatments.css
gives .surface-emphasis-rail: a 2px solid rule and a --space-4 inset. The
color is the role's, not shared, and it is always an emphasis accent, so
every set-apart passage reads as a boundary in both themes. A quotation is
represented voice, so its rail is the emphasis violet, declared as the violet
accent modifier declares it. A preformatted block's line structure is the
content, so it takes the magenta emphasis rail, declared exactly as the
emphasis rail declares it. The document's own callout — a thesis, a posed
question, a contrast — is .surface-emphasis-rail itself, magenta, composed
onto a document text role. Neutral is structure only: the hierarchy rail. */
.doc-quote {
--surface-emphasis-accent: var(--ask-emphasis-violet);
border-left: 2px solid var(--surface-emphasis-accent);
padding-left: var(--space-4);
}
.doc-pre {
--surface-emphasis-accent: var(--ask-emphasis-magenta);
border-left: 2px solid var(--surface-emphasis-accent);
padding-left: var(--space-4);
}
/* A passage inside a railed passage draws no rail of its own: the outer rail
already carries it, and a second rail would read as a second passage. So a
quoted block — a code excerpt someone else wrote, inside a .doc-quote — keeps
the one violet rail and takes no magenta rail, and a callout inside a
quotation draws no second rail. The selector is a descendant one, so it holds
through any container inside the outer passage; it outranks the emphasis
rail's own rule at (0,2,0). */
:is(.doc-quote, .doc-pre, .surface-emphasis-rail) :is(.doc-quote, .doc-pre, .surface-emphasis-rail) {
border-left: none;
padding-left: 0;
}
/* Two railed passages in a row would read as one passage. A passage directly
after a railed passage takes the same buffer surface-treatments.css gives a
rail after a rail, added to whatever gap its parent supplies. Passages inside
a railed passage draw no rails, so they take no buffer. A consumer that sets
passages side by side in a row resets this margin. */
:is(.doc-quote, .doc-pre, .surface-emphasis-rail):not(:is(.doc-quote, .doc-pre, .surface-emphasis-rail) *) + :is(.doc-quote, .doc-pre, .surface-emphasis-rail) { margin-top: var(--space-5); }
/* ---- Quotation -------------------------------------------------------- */
/* The quoted text is body-sized at every length: the rail sets it apart, not a
size step. The attribution takes the metadata metrics. */
.doc-quote { margin: 0; }
.doc-quote > p {
font-family: var(--font-sans);
font-size: var(--fs-body);
font-weight: var(--fw-extralight);
line-height: var(--lh-body);
letter-spacing: var(--tracking-normal);
color: var(--fg-1);
margin: 0;
}
.doc-quote > footer {
margin-top: var(--space-2);
font-family: var(--font-mono);
font-size: var(--fs-caption);
font-weight: var(--fw-light);
line-height: 1.4;
letter-spacing: var(--tracking-wide);
color: var(--fg-3);
}
/* ---- Preformatted and structured text --------------------------------- */
/* The block takes the primary text role: a diagram that is the argument of its
passage is payload, not apparatus. It scrolls itself rather than widening
its parent. */
.doc-pre {
font-family: var(--font-mono);
font-size: var(--fs-small);
font-weight: var(--fw-light);
line-height: var(--lh-body);
letter-spacing: var(--tracking-normal);
color: var(--fg-1);
white-space: pre;
overflow-x: auto;
margin: 0;
}
/* STRUCTURED. The block stays one scroll box; its parts carry the lines. Lines
that belong beneath a line sit in a .doc-hierarchy, one rail per level of
indentation, holding parts and further rails in source order. Inside the
block a rail's rhythm is the line slot, so its gap is zero. data-lead-lines
records the blank source lines before a part or a rail, at any depth, as
that many line heights.
PEER GROUPS. A .doc-pre-group is one declared peer group: its label, then
the lines beneath it. Successive groups sit exactly one line apart, whatever
their number, and inside a group nothing is set apart. A blank-line record
on a group's first line adds nothing: the one line between groups stands
for it, and before the first group it is dropped. */
.doc-pre--structured { white-space: normal; display: flex; flex-direction: column; }
.doc-pre--structured .doc-hierarchy { gap: 0; }
.doc-pre-part { margin: 0; font: inherit; color: inherit; white-space: pre; }
.doc-pre--structured [data-lead-lines="1"] { margin-top: 1lh; }
.doc-pre--structured [data-lead-lines="2"] { margin-top: 2lh; }
.doc-pre--structured [data-lead-lines="3"] { margin-top: 3lh; }
.doc-pre--structured > .doc-pre-group + .doc-pre-group { margin-top: 1lh; }
.doc-pre--structured > .doc-pre-group > :first-child { margin-top: 0; }
/* ---- Overflow cue ----------------------------------------------------- */
/* Shown only while a block, or a document table's box, is wider than its box.
Content hidden to the right fades at the right edge, and an operative label
beneath names the direction it scrolls. The label is sticky at the box's
left edge, and the fade is right-edge only, so the fade never masks the
label. */
.doc-pre[data-overflow-end],
.doc-table-scroll[data-overflow-end] {
-webkit-mask-image: linear-gradient(to right, #000 calc(100% - var(--space-6)), transparent);
mask-image: linear-gradient(to right, #000 calc(100% - var(--space-6)), transparent);
}
.doc-pre[data-overflow]::after,
.doc-table-scroll[data-overflow]::after {
display: block;
position: sticky;
left: 0;
margin-top: var(--space-2);
font-family: var(--font-mono);
font-size: var(--fs-caption);
font-weight: var(--fw-light);
line-height: var(--lh-tight);
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
color: var(--fg-3);
white-space: normal;