@cloudgrid-io/ui 0.27.2 → 0.29.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/README.md +8 -1
- package/dist/blocks/avatar.d.ts +10 -4
- package/dist/blocks/avatar.d.ts.map +1 -1
- package/dist/blocks/avatar.js +8 -2
- package/dist/blocks/avatar.js.map +1 -1
- package/dist/console.css +65 -0
- package/dist/fonts.css +38 -9
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/cloudgrid-cn.d.ts +1 -1
- package/dist/lib/cloudgrid-cn.d.ts.map +1 -1
- package/dist/lib/cloudgrid-cn.js +17 -0
- package/dist/lib/cloudgrid-cn.js.map +1 -1
- package/dist/theme.css +139 -0
- package/dist/tokens/base.json +489 -4
- package/dist/tokens/surface-ink.json +10 -0
- package/dist/tokens/surface-panel.json +10 -0
- package/dist/tokens.css +731 -11
- package/dist/tokens.json +53 -2
- package/dist/ui/button.d.ts +1 -1
- package/dist/ui/button.d.ts.map +1 -1
- package/dist/ui/button.js +28 -0
- package/dist/ui/button.js.map +1 -1
- package/dist/ui/dialog.d.ts +17 -1
- package/dist/ui/dialog.d.ts.map +1 -1
- package/dist/ui/dialog.js +18 -2
- package/dist/ui/dialog.js.map +1 -1
- package/dist/ui/drawer.d.ts +208 -0
- package/dist/ui/drawer.d.ts.map +1 -0
- package/dist/ui/drawer.js +265 -0
- package/dist/ui/drawer.js.map +1 -0
- package/package.json +3 -1
package/dist/tokens.css
CHANGED
|
@@ -443,10 +443,230 @@
|
|
|
443
443
|
value held on white, so the ground moved and the dot kept its contrast. The
|
|
444
444
|
muted text rung comes with it, 3.27:1 -> 3.69:1 on white. */
|
|
445
445
|
--cg-neutral-500: #858585;
|
|
446
|
+
/*
|
|
447
|
+
SOURCE: derived, 2026-09-18 (#232). The missing rung between neutral-500 and
|
|
448
|
+
neutral-700, and the only reason it exists is that --cg-text-muted has no value
|
|
449
|
+
on this ramp that clears the 4.5:1 body-text floor.
|
|
450
|
+
|
|
451
|
+
neutral-500 measures 3.69:1 on the card, 3.28:1 on the page ground and 3.29:1 on
|
|
452
|
+
the lavender wash. All three clear 3:1 for large text and none clears 4.5:1, so
|
|
453
|
+
every caption, every eyebrow and every tile hint in this library is under AA for
|
|
454
|
+
body copy. neutral-700 clears it everywhere (7.00:1 on the card) but it IS
|
|
455
|
+
--cg-text-secondary, so aliasing a "strong muted" onto it would not add a rung,
|
|
456
|
+
it would rename one.
|
|
457
|
+
|
|
458
|
+
Derived, with the sweep, so the next person does not repeat it:
|
|
459
|
+
|
|
460
|
+
#6e6e6e on the page ground : 4.53:1 clears by 0.03 — a margin a later
|
|
461
|
+
ground retune would erase in silence
|
|
462
|
+
#6c6c6c : 4.66:1
|
|
463
|
+
#6a6a6a : 4.80:1 <-- this
|
|
464
|
+
#686868 : 4.95:1
|
|
465
|
+
|
|
466
|
+
#6a6a6a on the card #ffffff : 5.41:1
|
|
467
|
+
on the page #f1f1f6 : 4.80:1
|
|
468
|
+
on raised #f4f6f8 : 4.99:1
|
|
469
|
+
on subtle #f2f1f7 : 4.82:1
|
|
470
|
+
|
|
471
|
+
The extra step past the first passing value is --cg-accent's practice, and for
|
|
472
|
+
its reason: 4.53:1 is a number that a page-ground retune turns into a failure
|
|
473
|
+
with nothing going red.
|
|
474
|
+
|
|
475
|
+
KNOWN, and not fixed here: on the violet-200 wash the frames draw (#d9d4fb) this
|
|
476
|
+
measures 3.80:1, still under the body floor. Nothing on a neutral ramp clears
|
|
477
|
+
4.5:1 on that ground and stays muted; see --cg-violet-200's own note. */
|
|
478
|
+
--cg-neutral-600: #6a6a6a;
|
|
446
479
|
/* SOURCE: legal / secondary text */
|
|
447
480
|
--cg-neutral-700: #595959;
|
|
448
481
|
/* SOURCE: ink */
|
|
449
482
|
--cg-neutral-900: #222222;
|
|
483
|
+
/*
|
|
484
|
+
SOURCE: Figma variable color/neutral/300, read first-hand on the thegrid frame
|
|
485
|
+
(8364:4396, "On the grid & to the world" on the ink band).
|
|
486
|
+
|
|
487
|
+
THE NAME IS NOT AN ACCIDENT. Figma calls this family color/neutral/300 / 500 /
|
|
488
|
+
600. This package's neutral ramp is already occupied at every one of those
|
|
489
|
+
numbers by a PURE grey (#acacac, #858585, #595959) that two published consumers
|
|
490
|
+
ship today, and these three are blue-greys: #828c98 is B152 against R130, which
|
|
491
|
+
is not a grey at all. Taking the Figma numbers would mean either changing three
|
|
492
|
+
shipped values (breaking) or having one ramp hold two hue families under one
|
|
493
|
+
name (silent). So the cool family takes its own root and the greys keep theirs.
|
|
494
|
+
|
|
495
|
+
1.45:1 on the card and 1.29:1 on the page ground: this is a value for text on a
|
|
496
|
+
dark ground, never on a light one. */
|
|
497
|
+
--cg-slate-300: #d2d7de;
|
|
498
|
+
/*
|
|
499
|
+
SOURCE: Figma variable color/neutral/500, also carried under the legacy name
|
|
500
|
+
"N3 new". Read first-hand on four of the six canonical frames — thegrid
|
|
501
|
+
(8364:3966, footer links), teams (7172:7607), pricing (7779:9284), builders
|
|
502
|
+
(6916:8104, the terminal's "$"). It is the design's quiet-text tone and the most
|
|
503
|
+
frequently missing value in this package (ui#239).
|
|
504
|
+
|
|
505
|
+
WHAT IT MEASURES, because the frames spend it on words:
|
|
506
|
+
|
|
507
|
+
on the card #ffffff : 3.41:1
|
|
508
|
+
on the page ground #f1f1f6 : 3.03:1
|
|
509
|
+
on raised #f4f6f8 : 3.15:1
|
|
510
|
+
on the violet wash #d9d4fb : 2.40:1 <-- under even the 3:1 indicator bar
|
|
511
|
+
on ink #222222 : 4.66:1
|
|
512
|
+
on the purple panel #5a47f0 : 1.71:1 invisible
|
|
513
|
+
|
|
514
|
+
So it clears 3:1 for large text on the three light grounds, clears 4.5:1 on ink
|
|
515
|
+
alone, and fails everywhere the frames actually use it for 15px footer links.
|
|
516
|
+
It is shipped because the frames use it and this package is meant to be able to
|
|
517
|
+
draw them; it is deliberately NOT wired to a --cg-text-* semantic, because those
|
|
518
|
+
carry a contrast contract and this value cannot meet it. --cg-slate-600 is the
|
|
519
|
+
companion that can. See ui#232 for the same shape of problem on --cg-text-muted.
|
|
520
|
+
|
|
521
|
+
DECLARED AT :root ONLY. A brand ramp rung is not a semantic and does not flip. */
|
|
522
|
+
--cg-slate-500: #828c98;
|
|
523
|
+
/*
|
|
524
|
+
SOURCE: Figma variable color/neutral/600, read first-hand on thegrid
|
|
525
|
+
(8364:4327, the stats rows; 8364:4255, "your CREATIONS").
|
|
526
|
+
|
|
527
|
+
This is the accessible companion to --cg-slate-500 and the design supplied it
|
|
528
|
+
itself, which is why nothing had to be invented:
|
|
529
|
+
|
|
530
|
+
on the card #ffffff : 5.65:1
|
|
531
|
+
on the page ground #f1f1f6 : 5.02:1
|
|
532
|
+
on raised #f4f6f8 : 5.21:1
|
|
533
|
+
on subtle #f2f1f7 : 5.03:1
|
|
534
|
+
on the cyan tint #dffbfc : 5.20:1
|
|
535
|
+
on the violet wash #d9d4fb : 3.97:1 large text only
|
|
536
|
+
on the lime #b6de68 : 3.67:1 large text only
|
|
537
|
+
on ink #222222 : 2.82:1 do not
|
|
538
|
+
on the purple panel #5a47f0 : 1.03:1 invisible
|
|
539
|
+
|
|
540
|
+
So: safe for body copy on any of the light page and card grounds, and on the
|
|
541
|
+
cyan tint. NOT safe on the violet wash, on the lime, or on either dark surface.
|
|
542
|
+
Where the frames put slate-500 on a light ground and mean it as running text,
|
|
543
|
+
this is the value that keeps the design and passes. */
|
|
544
|
+
--cg-slate-600: #5f6874;
|
|
545
|
+
/*
|
|
546
|
+
SOURCE: Figma variable "stone", declared on the home frame (8204:2397). ui#239
|
|
547
|
+
asks for it by name.
|
|
548
|
+
|
|
549
|
+
HONEST PROVENANCE, because this is the one value here that is weaker than the
|
|
550
|
+
rest: it is a DECLARATION. The home frame declares it as a variable, and the
|
|
551
|
+
Figma quota ran out before the audit could read the ~90 nodes of that frame that
|
|
552
|
+
might paint with it. No node on any of the other five frames uses it. So its
|
|
553
|
+
role is recorded as unknown rather than guessed, and nothing in this package
|
|
554
|
+
consumes it yet.
|
|
555
|
+
|
|
556
|
+
It is a warm near-black, not a grey: R52 G51 B48 against --cg-ink's flat #222222.
|
|
557
|
+
12.63:1 on the card and 11.22:1 on the page ground, so wherever it lands it is a
|
|
558
|
+
text-or-ink value and not a surface tint.
|
|
559
|
+
|
|
560
|
+
The rung number is by lightness against this package's own ramp, which ends at
|
|
561
|
+
neutral-900 #222222. Figma gives it no number. */
|
|
562
|
+
--cg-stone-800: #343330;
|
|
563
|
+
|
|
564
|
+
/* ---- hue-ramps ----
|
|
565
|
+
The three brand hues as RAMPS, not as single values (#239, 2026-09-18).
|
|
566
|
+
|
|
567
|
+
Until now this package carried one violet, one lime and one cyan, plus a single
|
|
568
|
+
--cg-primary-tint. The six canonical Figma frames carry a full scale of each,
|
|
569
|
+
declared as Figma variables under color/violet/*, color/lime/* and color/cyan/*,
|
|
570
|
+
and they spend those rungs on real surfaces: chips, role badges, avatar strokes,
|
|
571
|
+
step diagrams, the enterprise button. With no rung to reach for, a consumer
|
|
572
|
+
either hardcodes the hex, which CLAUDE.md forbids, or approximates with
|
|
573
|
+
color-mix(). cloudgrid-landing is doing the second right now — #133 renders
|
|
574
|
+
`color-mix(in srgb, var(--cg-primary) 22%, var(--cg-neutral-0))` to get within
|
|
575
|
+
2/255 of --cg-violet-200 — and that is the drift this group removes.
|
|
576
|
+
|
|
577
|
+
RUNG NUMBERS ARE FIGMA'S, not this package's, wherever Figma names them. Two
|
|
578
|
+
rungs (violet-150, violet-400, cyan-400) are values observed applied on a node
|
|
579
|
+
with NO Figma variable behind them; each says so in its own note and is placed
|
|
580
|
+
by lightness between the bound rungs either side. Nothing here was invented.
|
|
581
|
+
|
|
582
|
+
WHY THE UTILITY NAMES DIVERGE FROM THE TOKEN NAMES. The violet ramp maps onto
|
|
583
|
+
--color-brand-100..900, because "brand" is this package's own name for the hue
|
|
584
|
+
(theme.css, rule 4) and Tailwind ships no `brand` scale to collide with. The
|
|
585
|
+
cyan and lime rungs map onto SUFFIXED names (cyan-tint, lime-edge) rather than
|
|
586
|
+
cyan-200 / lime-700, because Tailwind DOES ship those exact numbered names and
|
|
587
|
+
claiming them would silently repaint every `bg-cyan-200` in a consumer's own
|
|
588
|
+
markup. That is rule 4's failure aimed at Tailwind instead of shadcn, and it is
|
|
589
|
+
worse, because the consumer never opted in.
|
|
590
|
+
|
|
591
|
+
DECLARED AT :root ONLY, like every brand source. A ramp rung is not a semantic
|
|
592
|
+
and does not flip; the semantics that spend it do. */
|
|
593
|
+
/*
|
|
594
|
+
SOURCE: Figma color/violet/100. Applied on thegrid at 8364:4136 (the private-status chip), 8364:4158 (the Owner role badge) and the Add-people button. 1.20:1 on the card, so a ground and never a foreground. */
|
|
595
|
+
--cg-violet-100: #ebe8fd;
|
|
596
|
+
/*
|
|
597
|
+
SOURCE: observed applied, UNBOUND. teams 7172:7759 (the first flow circle) and builders 6919:8782 (the first step circle, "Detect"). No Figma variable carries it.
|
|
598
|
+
|
|
599
|
+
It is the value ui#239 asks for and the one cloudgrid-landing currently reaches with a color-mix of --cg-primary. Placed at 150 because it sits between the two bound rungs either side: violet-100 #ebe8fd and violet-200 #d9d4fb. A half-rung is not new here; the neutral ramp already carries neutral-25 between 0 and 50.
|
|
600
|
+
|
|
601
|
+
Measured as a ground: --cg-text-primary is 12.38:1 on it, --cg-slate-600 4.25:1, --cg-slate-500 2.56:1. */
|
|
602
|
+
--cg-violet-150: #e3e0fd;
|
|
603
|
+
/*
|
|
604
|
+
SOURCE: Figma color/violet/200, declared on the home and thegrid frames.
|
|
605
|
+
|
|
606
|
+
READ ui#239'S CORRECTION BEFORE USING --cg-offline FOR THIS. That issue's first table maps #D9D4FB onto --cg-offline, and the mapping only holds inside [data-cg-surface='panel']. At :root --cg-offline is var(--cg-neutral-500), a mid grey, so a consumer taking that token at face value on the page ground gets #858585 and nothing goes red. This rung is the value; --cg-offline is a status tone that happens to reach it on one surface.
|
|
607
|
+
|
|
608
|
+
The pairing the frames draw on it is a documented AA failure and the reason --cg-slate-600 exists: --cg-slate-500 on this ground measures 2.40:1, under even the 3:1 indicator bar. --cg-text-primary is 11.18:1, --cg-slate-600 3.97:1 (large text only), --cg-neutral-600 3.80:1. */
|
|
609
|
+
--cg-violet-200: #d9d4fb;
|
|
610
|
+
/*
|
|
611
|
+
SOURCE: Figma color/violet/300, declared on thegrid. Rendered there only inside flattened SVG illustration, so it is a declared rung with no CSS-reachable node behind it yet. Carried so the ramp a designer edits in Figma is the ramp this package ships. */
|
|
612
|
+
--cg-violet-300: #c8c2fa;
|
|
613
|
+
/*
|
|
614
|
+
SOURCE: observed applied, UNBOUND. builders 6919:8785, the second step circle.
|
|
615
|
+
|
|
616
|
+
Placed at 400 because it is the only observed violet with no bound rung within reach: it sits between color/violet/300 #c8c2fa and color/violet/500 #a69bf7.
|
|
617
|
+
|
|
618
|
+
Worth knowing before retuning: the four builders step circles are #E3E0FD / #BCB4F9 / #A49AF7 / #8072F3, which are --cg-primary composited on white at 17 / 40 / 55 / 77 percent. The last two land within 2 and 4 of bound rungs 500 and 700 and are taken as those; this one does not. */
|
|
619
|
+
--cg-violet-400: #bcb4f9;
|
|
620
|
+
/*
|
|
621
|
+
SOURCE: Figma color/violet/500, declared on thegrid. builders 6919:8788 paints #A49AF7, which is this value within 2/255 per channel and is treated as this rung rather than as a ninth violet. */
|
|
622
|
+
--cg-violet-500: #a69bf7;
|
|
623
|
+
/*
|
|
624
|
+
SOURCE: Figma color/violet/600, declared on thegrid; rendered only inside flattened SVG illustration. Carried for the same reason as violet-300. */
|
|
625
|
+
--cg-violet-600: #9488f5;
|
|
626
|
+
/*
|
|
627
|
+
SOURCE: Figma color/violet/700. Applied on thegrid 8364:4388, the stroke around an avatar chip in the Plug section. builders 6919:8791 paints #8072F3, within 4/255 per channel, and is taken as this rung.
|
|
628
|
+
|
|
629
|
+
3.57:1 on the card: an indicator or a border, never a word. */
|
|
630
|
+
--cg-violet-700: #8476f4;
|
|
631
|
+
/*
|
|
632
|
+
Figma names --cg-primary color/violet/900. Deliberately an ALIAS rather than the hex written a second time: the brand hue has one home, at the top of this file, and a designer retuning it must not have to find two. */
|
|
633
|
+
--cg-violet-900: var(--cg-primary);
|
|
634
|
+
/*
|
|
635
|
+
SOURCE: Figma color/cyan/200, also carried under the legacy name "Teriary 20" (the typo is Figma's). Applied on three frames: thegrid 8364:4166 (the Editor role badge), teams 7172:7643 and pricing 7779:9320 (the footer's secondary buttons, with a --cg-tertiary hairline around them).
|
|
636
|
+
|
|
637
|
+
It IS a tint of the tertiary, and exactly: #60e9f0 composited on white at 20% is #dffbfc to the byte. Shipped as a value anyway rather than as a color-mix, because a consumer should not have to know the recipe to draw a button.
|
|
638
|
+
|
|
639
|
+
--cg-slate-600 measures 5.20:1 on it and --cg-text-primary 14.65:1. */
|
|
640
|
+
--cg-cyan-200: #dffbfc;
|
|
641
|
+
/*
|
|
642
|
+
SOURCE: Figma color/cyan/300, declared on thegrid; rendered only inside flattened SVG illustration. Carried so the ramp is whole. */
|
|
643
|
+
--cg-cyan-300: #cff8fa;
|
|
644
|
+
/*
|
|
645
|
+
SOURCE: observed applied, UNBOUND. thegrid 8364:4002 and 8364:4056 — the footer and enterprise buttons, drawn as this fill with a --cg-tertiary hairline. ui#239's second comment asks for it.
|
|
646
|
+
|
|
647
|
+
IT IS NOT A TINT OF --cg-tertiary, and that is worth the arithmetic, because every other rung in this family is. #60E9F0 has G 233, so reaching G 252 needs 86% white, and that mix gives R 233 — this value has R 177. Solving the same mix from the blue channel gives 0%. No single alpha produces it. So the color-mix() workaround available for the violet wash is not available here, which is precisely why the token is.
|
|
648
|
+
|
|
649
|
+
Placed at 400 by lightness, between bound rungs cyan-300 and cyan-900.
|
|
650
|
+
|
|
651
|
+
5.07:1 on the purple panel and 13.82:1 on ink: as a fill it carries text on both dark surfaces. 1.15:1 on the card, so never a foreground on white. */
|
|
652
|
+
--cg-cyan-400: #b1fcff;
|
|
653
|
+
/*
|
|
654
|
+
Figma names --cg-tertiary color/cyan/900. An alias, for the reason violet-900 is one. */
|
|
655
|
+
--cg-cyan-900: var(--cg-tertiary);
|
|
656
|
+
/*
|
|
657
|
+
SOURCE: Figma color/lime/200, declared on thegrid; rendered only inside flattened SVG illustration. Carried so the ramp is whole. */
|
|
658
|
+
--cg-lime-200: #eef7dc;
|
|
659
|
+
/*
|
|
660
|
+
SOURCE: Figma color/lime/600, declared on thegrid; rendered only inside flattened SVG illustration. */
|
|
661
|
+
--cg-lime-600: #d0ea9e;
|
|
662
|
+
/*
|
|
663
|
+
SOURCE: Figma color/lime/700. Applied on thegrid 8364:4387, the stroke around the audience avatars in the Plug section.
|
|
664
|
+
|
|
665
|
+
1.38:1 on the card and 4.22:1 on the purple panel: a border on a dark ground, not a line on a light one. */
|
|
666
|
+
--cg-lime-700: #c8e68e;
|
|
667
|
+
/*
|
|
668
|
+
Figma names --cg-secondary color/lime/900. An alias, for the reason violet-900 is one. */
|
|
669
|
+
--cg-lime-900: var(--cg-secondary);
|
|
450
670
|
|
|
451
671
|
/* ---- semantic-surfaces ---- */
|
|
452
672
|
--cg-surface-page: var(--cg-neutral-25);
|
|
@@ -476,6 +696,37 @@
|
|
|
476
696
|
--cg-text-primary: var(--cg-neutral-900);
|
|
477
697
|
--cg-text-secondary: var(--cg-neutral-700);
|
|
478
698
|
--cg-text-muted: var(--cg-neutral-500);
|
|
699
|
+
/*
|
|
700
|
+
The muted tone for text that is real content: a tile hint, an eyebrow that
|
|
701
|
+
carries meaning, a caption a reader has to read. ui#232, 2026-09-18.
|
|
702
|
+
|
|
703
|
+
--cg-text-muted is under the 4.5:1 body-text floor on three of the four grounds
|
|
704
|
+
(3.69:1 card, 3.28:1 page, 3.44:1 panel, 5.92:1 ink), which means every caption
|
|
705
|
+
in this library is AA-legal as large text and illegal as body copy. #232 asked
|
|
706
|
+
for a stronger rung rather than a retune, on the reasoning #149 used for
|
|
707
|
+
state's -strong: a tone tuned to recede and a tone tuned to be read are two
|
|
708
|
+
jobs, and collapsing them retunes both.
|
|
709
|
+
|
|
710
|
+
It is NOT --cg-text-secondary under a second name. Secondary is neutral-700
|
|
711
|
+
(7.00:1 on the card) and reads as a second voice; this is neutral-600, a new
|
|
712
|
+
rung derived for the floor and nothing else. Its note carries the sweep.
|
|
713
|
+
|
|
714
|
+
base #6a6a6a card 5.41:1 page 4.80:1 raised 4.99:1 subtle 4.82:1
|
|
715
|
+
panel rgba(255,255,255,0.86) on #5a47f0 : 4.75:1
|
|
716
|
+
ink rgba(255,255,255,0.68) on #222222 : 8.02:1
|
|
717
|
+
|
|
718
|
+
ON THE PANEL THIS RUNG IS BRIGHTER THAN --cg-text-secondary, and that is not a
|
|
719
|
+
mistake. Measured, the panel's own secondary (0.82 white) is 4.47:1, which is
|
|
720
|
+
under the floor by 0.03. Nothing dimmer than 0.84 white clears 4.5:1 on that
|
|
721
|
+
ground, so the panel cannot deliver a "muted that reads as body copy" below its
|
|
722
|
+
own secondary. Same shape as the note further up about a panel chip's word:
|
|
723
|
+
where the surface cannot deliver the ratio, the surface has to move, not the
|
|
724
|
+
foreground. A designer who dislikes the inversion changes --cg-surface-panel,
|
|
725
|
+
not this.
|
|
726
|
+
|
|
727
|
+
DECORATIVE CAPTIONS MAY KEEP --cg-text-muted. This rung does not deprecate it;
|
|
728
|
+
#232's own done-when says so. */
|
|
729
|
+
--cg-text-muted-strong: var(--cg-neutral-600);
|
|
479
730
|
--cg-text-placeholder: var(--cg-neutral-400);
|
|
480
731
|
--cg-text-accent: var(--cg-primary);
|
|
481
732
|
--cg-text-inverse: var(--cg-neutral-0);
|
|
@@ -523,16 +774,94 @@
|
|
|
523
774
|
The mono face is untouched: it is used only inside prompt and terminal surfaces
|
|
524
775
|
and the draft says nothing about it. */
|
|
525
776
|
/*
|
|
526
|
-
The display
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
777
|
+
The display face, and the one token in this file that names a font this package
|
|
778
|
+
does NOT ship. ui#240, 2026-09-18.
|
|
779
|
+
|
|
780
|
+
All six canonical Figma frames set every headline in TANKER: the hero (92px on
|
|
781
|
+
five frames, 120px on home), every section heading (56px), every card title.
|
|
782
|
+
Nothing else in the design uses it and it is used for nothing else.
|
|
783
|
+
|
|
784
|
+
THE BINARY CANNOT SHIP HERE, and the reason is a licence, not an oversight.
|
|
785
|
+
Tanker is under the ITF Free Font License v2.0, whose section 02 forbids making
|
|
786
|
+
the Font Software available "through another font website, font library,
|
|
787
|
+
marketplace, repository, download service, application or platform". This
|
|
788
|
+
package is public on registry.npmjs.org, installable by anyone with no
|
|
789
|
+
credentials, which is exactly what that clause names. Section 03 states the
|
|
790
|
+
remedy: "Any third party wishing to use the Font Software must obtain their own
|
|
791
|
+
copy directly from Fontshare and be independently bound by this License."
|
|
792
|
+
|
|
793
|
+
That is why Manrope is fine and Tanker is not. Manrope arrives as
|
|
794
|
+
@fontsource-variable/manrope under the Open Font License, which permits
|
|
795
|
+
redistribution. Fontshare publishes no fontsource package.
|
|
796
|
+
|
|
797
|
+
So the package owns the STACK and the consumer owns the FILE. Adding 'Tanker' at
|
|
798
|
+
the front is strictly additive in effect: a consumer that has not self-hosted it
|
|
799
|
+
falls straight through to Manrope and renders exactly what 0.27.2 rendered.
|
|
800
|
+
Before this, a consumer wanting the display face had to copy this whole fallback
|
|
801
|
+
chain verbatim into its own :root (cloudgrid-landing PR #132 did), and that copy
|
|
802
|
+
went stale the moment this package retired the condensed rung.
|
|
803
|
+
|
|
804
|
+
PENDING, and stated so rather than assumed: the 2026-09-10 re-skin (#197)
|
|
805
|
+
deliberately removed the display rung and set the whole system in Manrope.
|
|
806
|
+
Whether naming Tanker here reverses that decision is the founder's call, and it
|
|
807
|
+
is the open question at the end of #240. This change is written to be reversible
|
|
808
|
+
in one line if the answer is no.
|
|
809
|
+
|
|
810
|
+
NOT RETUNED, deliberately: --cg-display-line (1.06) and --cg-display-tracking
|
|
811
|
+
(0.012em) were tuned against Saira Semi Condensed 800. Every Tanker node in
|
|
812
|
+
every frame is leading-none with no tracking. See --cg-display-line-tight. */
|
|
813
|
+
--cg-font-display: 'Tanker', 'Manrope Variable', 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
814
|
+
/*
|
|
815
|
+
The body face. cloudgrid-landing#145, 2026-09-18.
|
|
816
|
+
|
|
817
|
+
Every body string in the canonical frames is SPACE GROTESK, and it was checked
|
|
818
|
+
on five of the six independently before it was believed: home 8204:2561 and
|
|
819
|
+
8204:2563, thegrid 8364:4213, builders 6913:7654, teams 7172:7571, enterprise
|
|
820
|
+
7037:779, pricing 7779:9261 — all 20px / 1.46, all the lede under a headline,
|
|
821
|
+
all in unrelated sections. The nav links and the card copy are the same face at
|
|
822
|
+
15px. This is the design's text face, not a stray on one frame.
|
|
823
|
+
|
|
824
|
+
IT SHIPS HERE, unlike Tanker. @fontsource-variable/space-grotesk is OFL-1.1,
|
|
825
|
+
which permits redistribution, so it arrives as a dependency and an @import in
|
|
826
|
+
fonts.css exactly the way Manrope does. See fonts.css.
|
|
827
|
+
|
|
828
|
+
THIS IS A VALUE CHANGE, not an addition, and it is the only one in this release.
|
|
829
|
+
Manrope is kept in the chain immediately behind it, so a consumer whose pipeline
|
|
830
|
+
somehow does not resolve the new dependency renders precisely what it rendered
|
|
831
|
+
before. Blast radius: every string in every consumer that has not overridden
|
|
832
|
+
this token changes face. Metrics differ — Space Grotesk is wider at the same
|
|
833
|
+
size — so a consumer with a tightly measured layout should look at it before
|
|
834
|
+
taking the release. cloudgrid-landing is the consumer that asked for it.
|
|
835
|
+
|
|
836
|
+
MANROPE DID NOT LEAVE. The frames keep it, and only for two jobs: the eyebrow
|
|
837
|
+
and the button label. Those now read --cg-font-ui, which is this token's
|
|
838
|
+
previous value unchanged, so neither of them moves. */
|
|
839
|
+
--cg-font-body: 'Space Grotesk Variable', 'Space Grotesk', 'Manrope Variable', 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
840
|
+
/*
|
|
841
|
+
The face for the small fixed chrome of a page: the eyebrow above a heading, and
|
|
842
|
+
the label inside a button. 2026-09-18.
|
|
843
|
+
|
|
844
|
+
A third family stack is not a flourish. The canonical frames use exactly three
|
|
845
|
+
faces in three jobs — Tanker for display, Space Grotesk for everything a reader
|
|
846
|
+
reads, and MANROPE for the eyebrow and the button label — and they are
|
|
847
|
+
consistent about it. Every button label on all six frames is Manrope Bold 15
|
|
848
|
+
leading-none (home I8204:2580;732:46, thegrid I8364:4215;732:46, builders
|
|
849
|
+
I6913:7656;732:46, teams I7172:7573;732:46, enterprise I7037:781;732:46,
|
|
850
|
+
pricing I7779:9336;732:46). Every applied eyebrow is Manrope SemiBold 14, and
|
|
851
|
+
Figma binds it as a named text variable, "Eyebrow".
|
|
852
|
+
|
|
853
|
+
Its VALUE is --cg-font-body's previous value, byte for byte. That is what makes
|
|
854
|
+
the body-face change safe to read: the eyebrow and the button did not move, they
|
|
855
|
+
just stopped being spelled with the body token.
|
|
856
|
+
|
|
857
|
+
KNOWN INCONSISTENCY IN THE SOURCE, recorded so nobody "fixes" it from the wrong
|
|
858
|
+
end: on the home frame the four nav links (8204:2573) are Manrope Medium 15 and
|
|
859
|
+
"Sign In" beside them (8204:2579) is Space Grotesk Medium 15 — same slot, same
|
|
860
|
+
size, same weight, two faces. On thegrid the same split appears at 8364:4053
|
|
861
|
+
against 8364:4052/4054/4055. Five frames agree the nav is Space Grotesk, so the
|
|
862
|
+
Manrope instances read as a migration caught mid-flight rather than as intent,
|
|
863
|
+
and this token is NOT the nav's face. */
|
|
864
|
+
--cg-font-ui: 'Manrope Variable', 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
536
865
|
--cg-font-mono: 'JetBrains Mono Variable', 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
537
866
|
|
|
538
867
|
/* ---- weights ----
|
|
@@ -560,14 +889,221 @@
|
|
|
560
889
|
H1 display ramp: uppercase, condensed, Primary or white
|
|
561
890
|
|
|
562
891
|
Stepped down one notch 2026-08-06 (operator: main headings read too
|
|
563
|
-
dominant). The SOURCE values were xl 64 / lg 48 / md 42 / sm 32.
|
|
892
|
+
dominant). The SOURCE values were xl 64 / lg 48 / md 42 / sm 32.
|
|
893
|
+
|
|
894
|
+
2026-09-19 (#248): display-64 and display-48 added, named by pixel value — see
|
|
895
|
+
display-64's note. With the eight "SET FONT TO TANKER" placeholder nodes
|
|
896
|
+
excluded, ALL 68 remaining runs at 56 across all six frames are explicit
|
|
897
|
+
leading-none with zero tracking, as are all of 48, 36 and 32. #248's comment
|
|
898
|
+
reports 56 carrying two ratios and two trackings; that was one un-converted Anton
|
|
899
|
+
node (home 8216:1762) and three of its runs. This does NOT reopen the proposal to
|
|
900
|
+
move --cg-display-line and --cg-display-tracking: 26 (77 runs) genuinely carries
|
|
901
|
+
1.0, 1.1 and Auto, so no single library default is right for it, and
|
|
902
|
+
--cg-display-line-tight already covers the cases that want 1. Both shipped values
|
|
903
|
+
stay exactly as they are. */
|
|
904
|
+
/*
|
|
905
|
+
SOURCE: home 8204:2556, the hero headline, Tanker at leading-none.
|
|
906
|
+
|
|
907
|
+
The largest rung in the system and the only node that asks for it. It is kept
|
|
908
|
+
separate from display-2xl rather than collapsed onto it because the two are 28px
|
|
909
|
+
apart and both are heroes: home is 120 and the other five frames are 92. */
|
|
910
|
+
--cg-display-3xl: 120px;
|
|
911
|
+
/*
|
|
912
|
+
SOURCE: the hero headline on five of the six canonical frames — thegrid
|
|
913
|
+
8364:4211, builders 6913:7652, teams 7172:7569, enterprise 7037:777, pricing
|
|
914
|
+
7779:9259. Tanker, leading-none, no tracking.
|
|
915
|
+
|
|
916
|
+
This is the ramp's real top. Before it, --cg-display-xl (56px) was the largest
|
|
917
|
+
value in the package and every hero in the design was 36px above it, so each
|
|
918
|
+
consumer clamped its own. thegrid also draws a 96px closing CTA (8364:3957) and
|
|
919
|
+
a 64px section heading (8364:4363); both are single nodes and are deliberately
|
|
920
|
+
not rungs. A value invented to complete a grid is the thing this file keeps
|
|
921
|
+
refusing to add. */
|
|
922
|
+
--cg-display-2xl: 92px;
|
|
923
|
+
/*
|
|
924
|
+
SOURCE: home 8204:3110 ("WHERE DOES IT LIVE?"), thegrid 8364:4363 ("Say what it
|
|
925
|
+
needs"). Two nodes, two frames, Tanker, no tracking. The widest hole in the ramp:
|
|
926
|
+
36px separate --cg-display-xl 56 from --cg-display-2xl 92, and both consumers
|
|
927
|
+
were clamping their own value into it.
|
|
928
|
+
|
|
929
|
+
NAMED BY PIXEL VALUE, 2026-09-19 (#248). Every size added in this round falls
|
|
930
|
+
STRICTLY BETWEEN two shipped rungs, which is the second consecutive census to
|
|
931
|
+
hit that wall — the tanker-title-ramp note records the first. A t-shirt suffix
|
|
932
|
+
encodes ORDER, and order is what an insertion breaks; renaming a shipped rung is
|
|
933
|
+
breaking for two published consumers, and a third Tanker ramp would carry the
|
|
934
|
+
same face, leading and roles as the two that exist and therefore no information.
|
|
935
|
+
So the ramp prefix still means (face, leading, role) and the suffix becomes the
|
|
936
|
+
size. A px-named rung cannot be made wrong by a later insertion.
|
|
937
|
+
|
|
938
|
+
SIZE ONLY, AND DELIBERATELY.
|
|
939
|
+
|
|
940
|
+
A rung gets a --text-* utility only where the frames settled on a leading. Where
|
|
941
|
+
they did not, it ships as a custom property alone: theme.css's own rule is that
|
|
942
|
+
"a companion that invents a value is worse than an absent one", and
|
|
943
|
+
tokens.test.mjs requires every --text-* to carry a --line-height, so a utility
|
|
944
|
+
here could only exist by inventing one.
|
|
945
|
+
|
|
946
|
+
The two nodes do not agree on a leading and only one of them expresses an
|
|
947
|
+
opinion: thegrid sets 70px (ratio 1.094) and home leaves lineHeightUnit at
|
|
948
|
+
INTRINSIC_%, i.e. Figma's Auto, which is Tanker's own 1.35 default and not a
|
|
949
|
+
design decision at all. A leading shipped here would be wrong for one of its two
|
|
950
|
+
users. The same reasoning is why 96px (thegrid 8364:3957, lh 56 — a ratio of
|
|
951
|
+
0.583, 96px type in a 56px line box) is recorded and still has no rung. */
|
|
952
|
+
--cg-display-64: 64px;
|
|
564
953
|
--cg-display-xl: 56px;
|
|
954
|
+
/*
|
|
955
|
+
SOURCE: home 8216:1868, the "Copy this command INTO your aI tool to Plug in"
|
|
956
|
+
statement. Tanker, explicit leading-none (lineHeightUnit FONT_SIZE_% at 100), no
|
|
957
|
+
tracking.
|
|
958
|
+
|
|
959
|
+
ONE NODE ON ONE FRAME, stated plainly because the count in #248 reads as four:
|
|
960
|
+
the node carries four per-character colour runs and a run census counts each of
|
|
961
|
+
them. One node is not disqualifying here — --cg-display-3xl 120 is also a single
|
|
962
|
+
node on a single frame and 0.28.0 shipped it — but the evidence is one headline,
|
|
963
|
+
not four.
|
|
964
|
+
|
|
965
|
+
Named by pixel value; --cg-display-64's note has the reasoning (#248).
|
|
966
|
+
|
|
967
|
+
It carries display-line-tight and title-tracking rather than --cg-display-line
|
|
968
|
+
and --cg-display-tracking, the same as --cg-display-2xl and --cg-display-3xl:
|
|
969
|
+
the node is explicitly 1.0 / 0, and the two legacy values were tuned for a
|
|
970
|
+
condensed face this package no longer ships (#240). */
|
|
971
|
+
--cg-display-48: 48px;
|
|
565
972
|
--cg-display-lg: 44px;
|
|
566
973
|
/* SOURCE: onboarding H1 (was 42px) */
|
|
567
974
|
--cg-display-md: 38px;
|
|
568
975
|
--cg-display-sm: 30px;
|
|
569
976
|
--cg-display-line: 1.06;
|
|
570
977
|
--cg-display-tracking: 0.012em;
|
|
978
|
+
/*
|
|
979
|
+
The leading every Tanker node in every canonical frame actually uses: 1, i.e.
|
|
980
|
+
leading-none. Not one display node in six frames emits a letter-spacing either.
|
|
981
|
+
|
|
982
|
+
--cg-display-line stays 1.06 and --cg-display-tracking stays 0.012em. Both were
|
|
983
|
+
tuned against Saira Semi Condensed 800 (ui#240 records this), neither has been
|
|
984
|
+
retuned for Tanker, and both are shipped values that four display rungs and
|
|
985
|
+
every consumer's display utility already read. Retuning them is a one-line edit
|
|
986
|
+
and a visible change to text that exists today, so it is proposed rather than
|
|
987
|
+
made. This rung is what the new sizes are built on in the meantime. */
|
|
988
|
+
--cg-display-line-tight: 1;
|
|
989
|
+
|
|
990
|
+
/* ---- tanker-title-ramp ----
|
|
991
|
+
The display face at CARD scale, 2026-09-18.
|
|
992
|
+
|
|
993
|
+
Four rungs the canonical frames use constantly and this package could spell
|
|
994
|
+
none of. They are separated from h1-display-ramp rather than wedged into it
|
|
995
|
+
because the two ramps disagree everywhere they overlap, and the disagreement is
|
|
996
|
+
not resolvable by addition:
|
|
997
|
+
|
|
998
|
+
frames 120 96 92 64 56 40 38 32 30 26 20
|
|
999
|
+
package 56 44 38 30
|
|
1000
|
+
|
|
1001
|
+
56, 38 and 30 coincide. 44 is used by nothing in any of the six frames. Every
|
|
1002
|
+
other size the frames ask for falls BETWEEN two shipped rungs — 40 between 44
|
|
1003
|
+
and 38, 26 between 30 and heading-lg's 22 — so there is no name left on the
|
|
1004
|
+
t-shirt ramp that is both free and in the right place, and renaming a shipped
|
|
1005
|
+
rung is a breaking change for two published consumers.
|
|
1006
|
+
|
|
1007
|
+
They are also not headings. --cg-heading-* is the Manrope ramp at 1.2 leading;
|
|
1008
|
+
this is the display FACE at leading-none, and --cg-title-sm and
|
|
1009
|
+
--cg-heading-md are both 20px precisely because a face and a leading are what
|
|
1010
|
+
separate them, not a size.
|
|
1011
|
+
|
|
1012
|
+
SIZES, with where they were read:
|
|
1013
|
+
40 home 8204:2589, enterprise 7038:1244, pricing 7785:9809, thegrid 8364:4382
|
|
1014
|
+
26 teams 7297:8606, builders 6916:8648, enterprise 7038:1192,
|
|
1015
|
+
pricing 7788:10264, thegrid 8364:3882
|
|
1016
|
+
20 teams 7172:7760, builders 6919:8783, thegrid petal labels
|
|
1017
|
+
|
|
1018
|
+
pricing 7785:10059 draws one 32px Tanker number and thegrid draws 15px Tanker
|
|
1019
|
+
eyebrows. Single-frame, so recorded and not given a rung.
|
|
1020
|
+
|
|
1021
|
+
---
|
|
1022
|
+
|
|
1023
|
+
2026-09-19 (#248). Three more rungs — title-36, title-32, title-24 — and the
|
|
1024
|
+
t-shirt suffix stops here. See --cg-display-64's note for why they are named by
|
|
1025
|
+
pixel value and the group note above for why this ramp is separate from
|
|
1026
|
+
h1-display-ramp in the first place.
|
|
1027
|
+
|
|
1028
|
+
RECORDED AND DELIBERATELY NOT GIVEN A RUNG:
|
|
1029
|
+
|
|
1030
|
+
28px. #248 proposes it on two home nodes. Both are placeholders the designer
|
|
1031
|
+
has not converted: 8204:3066 "lead - SET FONT TO TANKER" (lineHeight 92.6%,
|
|
1032
|
+
letterSpacing 0.24) and 8216:1784 "txt:RIGHT HERE - SET FONT TO TANKER"
|
|
1033
|
+
(106%, 0.42). They keep the previous face's metrics — a third node in the same
|
|
1034
|
+
set, 8216:1762, is still literally set in Anton — so there is no settled 28px
|
|
1035
|
+
anything in the six frames. Six home nodes and two thegrid nodes carry that
|
|
1036
|
+
"SET FONT TO TANKER" marker; every Tanker node in the frames that emits a
|
|
1037
|
+
letter-spacing is one of them.
|
|
1038
|
+
|
|
1039
|
+
32px on Inter (builders 7172:8458, the "$ grid plug" pill). Off-system in the
|
|
1040
|
+
design itself; #243 recorded it.
|
|
1041
|
+
|
|
1042
|
+
ON AUTO LEADING. Figma's lineHeightUnit distinguishes a decision from an
|
|
1043
|
+
absence: FONT_SIZE_% and PIXELS mean the designer set a leading, INTRINSIC_%
|
|
1044
|
+
means Auto and the number in the file is the FACE's default, which for Tanker is
|
|
1045
|
+
1.35. Every 1.35 in a Tanker census is INTRINSIC_%. A rung built on one would
|
|
1046
|
+
encode Tanker's metrics as a brand decision. */
|
|
1047
|
+
/*
|
|
1048
|
+
Four frames. The governance card title, the plan price, the sub-heading under a section statement. */
|
|
1049
|
+
--cg-title-lg: 40px;
|
|
1050
|
+
/*
|
|
1051
|
+
SOURCE: enterprise 7039:5124, 7039:5119, 7039:5129 — the three audience column
|
|
1052
|
+
headings ("Platform and security teams", "Innovation Leaders", "Business
|
|
1053
|
+
functions"). Three nodes, one frame, all explicit leading-none, no tracking.
|
|
1054
|
+
|
|
1055
|
+
One frame, and 2px from --cg-display-md 38. It is a rung rather than an
|
|
1056
|
+
approximation of 38 because it is the card/column role, not the section-heading
|
|
1057
|
+
role, and because rounding a measured 36 onto 38 is the kind of "close enough"
|
|
1058
|
+
that #243 had to unpick when ui#239's table called this ramp's 26 a "24px card
|
|
1059
|
+
title".
|
|
1060
|
+
|
|
1061
|
+
Named by pixel value; --cg-display-64's note has the reasoning (#248). */
|
|
1062
|
+
--cg-title-36: 36px;
|
|
1063
|
+
/*
|
|
1064
|
+
SOURCE: home 8204:2590 ("Learn how the grid can help you"), pricing 7785:10059 /
|
|
1065
|
+
10068 / 10073 / 10076 / 10081 / 10084 (the six plan prices). Seven nodes, two
|
|
1066
|
+
frames, all explicit leading-none, no tracking.
|
|
1067
|
+
|
|
1068
|
+
THE DISPLAY FACE HALF OF 32. The frames also draw 32 on the UI face, ten times,
|
|
1069
|
+
and that is --cg-stat-size on the Manrope ramp, not this. Two rungs at one size
|
|
1070
|
+
is the ramp rule working as intended, exactly as --cg-title-sm and
|
|
1071
|
+
--cg-heading-md are both 20px: a rung is a face, a leading and a role, not a
|
|
1072
|
+
number.
|
|
1073
|
+
|
|
1074
|
+
#243 recorded pricing's 32 as "one 32px Tanker number, single-frame, so recorded
|
|
1075
|
+
and not given a rung". The rebuilt extract finds seven nodes on two frames.
|
|
1076
|
+
|
|
1077
|
+
Named by pixel value; --cg-display-64's note has the reasoning (#248). */
|
|
1078
|
+
--cg-title-32: 32px;
|
|
1079
|
+
/*
|
|
1080
|
+
Five frames. The card title and the FAQ question — the most common display size in the design after the section heading.
|
|
1081
|
+
|
|
1082
|
+
ui#239's type table calls this rung "24px card title". Measured on five frames it is 26, not 24, and nothing in any frame is 24. The table was written from a consumer's approximation rather than from the node. */
|
|
1083
|
+
--cg-title-md: 26px;
|
|
1084
|
+
/*
|
|
1085
|
+
SOURCE: home 8204:2813, 8204:2753 / 2758 / 2763; enterprise 7039:5084, 7039:5088,
|
|
1086
|
+
7039:5093, 7039:5097, 7395:666. Twelve nodes, two frames. Nine set an explicit
|
|
1087
|
+
leading-none; two leave lineHeightUnit at INTRINSIC_% (Figma's Auto, no
|
|
1088
|
+
decision); one, home 8216:1908, sets 30px. No node carries tracking.
|
|
1089
|
+
|
|
1090
|
+
TWO PIXELS FROM --cg-title-md 26, AND BOTH ARE REAL. 26 is 77 runs on six frames
|
|
1091
|
+
and 24 is 12 on two; they coexist in the same design and in some cases in the
|
|
1092
|
+
same section. Note that #239's type table called the 26 rung "24px card title"
|
|
1093
|
+
from a consumer's approximation — this rung is the actual 24, measured, and does
|
|
1094
|
+
not retroactively make that table right.
|
|
1095
|
+
|
|
1096
|
+
Named by pixel value; --cg-display-64's note has the reasoning (#248). */
|
|
1097
|
+
--cg-title-24: 24px;
|
|
1098
|
+
/*
|
|
1099
|
+
Three frames. The label inside a step circle or a diagram petal. Same size as --cg-heading-md and a different face and leading; see the group note. */
|
|
1100
|
+
--cg-title-sm: 20px;
|
|
1101
|
+
/*
|
|
1102
|
+
leading-none, aliased rather than repeated so a retune of the display leading reaches both ramps. An alias compiles to var(), so it survives a scope flip. */
|
|
1103
|
+
--cg-title-line: var(--cg-display-line-tight);
|
|
1104
|
+
/*
|
|
1105
|
+
No Tanker node in any of the six frames emits a letter-spacing. Spelled out rather than omitted so a utility built on this ramp cannot inherit --cg-display-tracking's 0.012em by accident. */
|
|
1106
|
+
--cg-title-tracking: 0em;
|
|
571
1107
|
|
|
572
1108
|
/* ---- manrope-ramp ---- */
|
|
573
1109
|
/*
|
|
@@ -589,12 +1125,74 @@
|
|
|
589
1125
|
extrapolation, and a heading is short enough that 1.2 does not cost a line its
|
|
590
1126
|
descenders. */
|
|
591
1127
|
--cg-heading-line: 1.2;
|
|
1128
|
+
/*
|
|
1129
|
+
SOURCE: the lede paragraph on all six canonical frames — home 8204:2561,
|
|
1130
|
+
thegrid 8364:4213, builders 6913:7654, teams 7172:7571, enterprise 7037:779,
|
|
1131
|
+
pricing 7779:9261. Space Grotesk regular, line-height 1.46, no tracking.
|
|
1132
|
+
|
|
1133
|
+
The single most-used text size in the whole design and the one this package
|
|
1134
|
+
could not spell. --cg-body-lg is 19px and --cg-heading-md is 20px, so a
|
|
1135
|
+
consumer building a lede had to pick between a body token one pixel short and a
|
|
1136
|
+
heading token that is the right size with the wrong role and the wrong leading
|
|
1137
|
+
(1.2 against 1.46). cloudgrid-landing#145 records sections doing the first.
|
|
1138
|
+
|
|
1139
|
+
It reads --cg-body-line, so it is a body rung and not a fifth display rung.
|
|
1140
|
+
|
|
1141
|
+
pricing 7785:9832 draws one 18px bold plan name. Single node, one frame, 1px
|
|
1142
|
+
off --cg-body-lg: recorded, not given a rung. */
|
|
1143
|
+
--cg-body-xl: 20px;
|
|
592
1144
|
/* SOURCE: H1 subline */
|
|
593
1145
|
--cg-body-lg: 19px;
|
|
1146
|
+
/*
|
|
1147
|
+
SOURCE: pricing 7785:9832 / 9858 / 9872, the three plan names — Space Grotesk
|
|
1148
|
+
Bold 18 at line-height 1.46, which is --cg-body-line exactly. Also thegrid
|
|
1149
|
+
8364:4352 / 4355 / 4358 and 8364:4385, Manrope 18 at 1.5.
|
|
1150
|
+
|
|
1151
|
+
A BODY RUNG, NOT A HEADING ONE. #248 asks which ramp 18 belongs on, since it
|
|
1152
|
+
sits between --cg-heading-sm 17 and --cg-heading-md 20. Every node at 18 that
|
|
1153
|
+
expresses a leading at all reads 1.46 or 1.5; nothing is near
|
|
1154
|
+
--cg-heading-line's 1.2. home 8204:3090 and 8204:3105 draw Tanker at 18 and both
|
|
1155
|
+
are INTRINSIC_% (Auto), so they express no opinion and do not make this a
|
|
1156
|
+
display rung either.
|
|
1157
|
+
|
|
1158
|
+
#243 recorded pricing's 18 as "one 18px bold plan name. Single node, one frame,
|
|
1159
|
+
1px off --cg-body-lg". It is three nodes, and the 1px is the point: a consumer
|
|
1160
|
+
otherwise rounds it onto --cg-body-lg 19 and the plan-name row drifts by a pixel
|
|
1161
|
+
against the Figma it is being checked against.
|
|
1162
|
+
|
|
1163
|
+
Named by pixel value; --cg-display-64's note has the reasoning (#248). */
|
|
1164
|
+
--cg-body-18: 18px;
|
|
594
1165
|
/* SOURCE: default UI text */
|
|
595
1166
|
--cg-body-md: 16px;
|
|
596
1167
|
/* SOURCE: card body, legal */
|
|
597
1168
|
--cg-body-sm: 15px;
|
|
1169
|
+
/*
|
|
1170
|
+
SOURCE: home 8216:1768, 8216:1769, 8216:1771 — the three lines of the prompt-and-
|
|
1171
|
+
reply mock. Manrope Regular 14. Three runs, one frame.
|
|
1172
|
+
|
|
1173
|
+
READ THIS BEFORE TREATING 14 AS A GAP. #248 counts nineteen 14px runs on four
|
|
1174
|
+
frames and calls it one of its two strongest cases. SIXTEEN OF THE NINETEEN ARE
|
|
1175
|
+
THE EYEBROW — Manrope 600, leading 1.2, tracking 1.4px, uppercase — which 0.28.0
|
|
1176
|
+
already ships whole as --cg-eyebrow-size / -line / -tracking / -weight, and
|
|
1177
|
+
three of the nodes the issue cites (teams 7172:8463, builders 6916:8053,
|
|
1178
|
+
enterprise 7038:1184) are the very nodes in that token's own docblock. 14 was
|
|
1179
|
+
already closed; the four frames are the eyebrow's four frames.
|
|
1180
|
+
|
|
1181
|
+
So this rung exists for the residue, and the residue is thin. It ships anyway
|
|
1182
|
+
because without it the only 14 in the package is --cg-eyebrow-size, and a
|
|
1183
|
+
consumer who wants plain 14px text will reach for it and sooner or later reach
|
|
1184
|
+
for text-eyebrow with it, inheriting a weight and a tracking nobody asked for.
|
|
1185
|
+
--cg-mono-size is also 14 and is the mono face.
|
|
1186
|
+
|
|
1187
|
+
SIZE ONLY.
|
|
1188
|
+
|
|
1189
|
+
A rung gets a --text-* utility only where the frames settled on a leading. Where
|
|
1190
|
+
they did not, it ships as a custom property alone: theme.css's own rule is that
|
|
1191
|
+
"a companion that invents a value is worse than an absent one", and
|
|
1192
|
+
tokens.test.mjs requires every --text-* to carry a --line-height, so a utility
|
|
1193
|
+
here could only exist by inventing one. The three runs read 1.295 and 1.55; there is nothing
|
|
1194
|
+
to ship. */
|
|
1195
|
+
--cg-body-14: 14px;
|
|
598
1196
|
/* SOURCE: footer, helper */
|
|
599
1197
|
--cg-caption: 13px;
|
|
600
1198
|
/*
|
|
@@ -609,6 +1207,78 @@
|
|
|
609
1207
|
--cg-mono-line: 1.7;
|
|
610
1208
|
--cg-label-size: 15px;
|
|
611
1209
|
--cg-label-weight: var(--cg-weight-medium);
|
|
1210
|
+
/*
|
|
1211
|
+
SOURCE: Figma's own bound text variable, "Eyebrow" —
|
|
1212
|
+
Font(family: "Manrope", style: SemiBold, size: 14, weight: 600, lineHeight: 1.2,
|
|
1213
|
+
letterSpacing: 10). Applied first-hand on teams 7172:8463, builders 6916:8053
|
|
1214
|
+
and enterprise 7038:1184, each rendering tracking-[1.4px] uppercase.
|
|
1215
|
+
|
|
1216
|
+
The only rung in this package derived from a Figma TEXT variable rather than
|
|
1217
|
+
from a measured node, and it agrees with the nodes, which is why it is trusted.
|
|
1218
|
+
|
|
1219
|
+
It is equidistant from the two tokens a consumer would otherwise reach for:
|
|
1220
|
+
--cg-caption is 13px and --cg-label-size is 15px, and Eyebrow currently builds
|
|
1221
|
+
on the second. One pixel is not the point — the eyebrow also carries a weight,
|
|
1222
|
+
a leading and a tracking that neither of those tokens has, and spelling three
|
|
1223
|
+
of the four inline is how a component drifts.
|
|
1224
|
+
|
|
1225
|
+
KNOWN: thegrid draws its eyebrows differently — Tanker regular 15px, leading
|
|
1226
|
+
16px, tracking 1.1px (8364:3905, 8364:4409) — and both of pricing's eyebrows are
|
|
1227
|
+
switched off in the file. So this is the eyebrow of three frames, not six, and
|
|
1228
|
+
thegrid's is an open question for the designer rather than a second rung here. */
|
|
1229
|
+
--cg-eyebrow-size: 14px;
|
|
1230
|
+
/*
|
|
1231
|
+
From the Eyebrow variable. The same number as --cg-heading-line, spelled out rather than aliased: an eyebrow is not a heading, and a heading retune must not silently move it. */
|
|
1232
|
+
--cg-eyebrow-line: 1.2;
|
|
1233
|
+
/*
|
|
1234
|
+
From the Eyebrow variable, whose letterSpacing is 10 — Figma's percent, not pixels. At 14px that is 1.4px, which is what all three frames render. Stored in em so the ratio survives a size change. */
|
|
1235
|
+
--cg-eyebrow-tracking: 0.1em;
|
|
1236
|
+
/*
|
|
1237
|
+
600, from the Eyebrow variable's weight: 600.
|
|
1238
|
+
|
|
1239
|
+
Aliased onto --cg-weight-bold rather than written as a number, because 600 is
|
|
1240
|
+
already this package's "bold" (the weights group's own note explains why that
|
|
1241
|
+
name and not "semibold"). Two spellings of one weight is how a ramp starts
|
|
1242
|
+
disagreeing with itself.
|
|
1243
|
+
|
|
1244
|
+
Note this is NOT --cg-label-weight, which is 500. The eyebrow and the label are
|
|
1245
|
+
different rungs; see the group note. */
|
|
1246
|
+
--cg-eyebrow-weight: var(--cg-weight-bold);
|
|
1247
|
+
/*
|
|
1248
|
+
The big figure in a stat block: the UI face at leading-none.
|
|
1249
|
+
|
|
1250
|
+
SOURCE: home 8204:2769 / 2774 / 2779 / 2784 / 2789 and enterprise 7038:4564 /
|
|
1251
|
+
4569 / 4574 / 4579 / 4584 — "2", "184", "4", "8", "$0". Ten nodes, two frames,
|
|
1252
|
+
Manrope Medium 32, every one of them explicit leading-none with no tracking.
|
|
1253
|
+
|
|
1254
|
+
THE UI-FACE HALF OF 32; --cg-title-32 is the display-face half. This is a role
|
|
1255
|
+
the Manrope ramp could not spell at all: --cg-heading-* is 1.2 and --cg-body-* is
|
|
1256
|
+
1.46, and nothing on this face was at 1.
|
|
1257
|
+
|
|
1258
|
+
It is a role set rather than a bare size, on the --cg-eyebrow-* pattern, for the
|
|
1259
|
+
reason that block gives: the figure carries a leading and a weight as well as a
|
|
1260
|
+
size, and spelling two of the three inline is how a component drifts.
|
|
1261
|
+
|
|
1262
|
+
KNOWN DIVERGENCE, not fixed here: StatTile renders its value as
|
|
1263
|
+
`font-display text-display-sm` — Tanker 30 at --cg-display-line. The frames say
|
|
1264
|
+
Manrope Medium 32 at leading-none. Repointing it changes what every StatTile
|
|
1265
|
+
renders today, so it is a separate change with its own changeset.
|
|
1266
|
+
|
|
1267
|
+
The same five figures appear again at 18.149px inside scaled product mocks on
|
|
1268
|
+
home, thegrid and enterprise. That is the per-mock scale factor #248 documents,
|
|
1269
|
+
not a second rung. */
|
|
1270
|
+
--cg-stat-size: 32px;
|
|
1271
|
+
/*
|
|
1272
|
+
leading-none, from all ten nodes. Spelled as a number rather than aliased onto
|
|
1273
|
+
--cg-display-line-tight: that alias exists so a retune of the DISPLAY leading
|
|
1274
|
+
reaches the title ramp too, and the stat figure is on the UI face and must not
|
|
1275
|
+
move with it. */
|
|
1276
|
+
--cg-stat-line: 1;
|
|
1277
|
+
/*
|
|
1278
|
+
500, from all ten nodes. Manrope Medium, not the eyebrow's 600 and not
|
|
1279
|
+
--cg-label-weight's 500 by coincidence — aliased onto the weight rung so it
|
|
1280
|
+
tracks the brand's Medium. */
|
|
1281
|
+
--cg-stat-weight: var(--cg-weight-medium);
|
|
612
1282
|
|
|
613
1283
|
/* ---- spacing ----
|
|
614
1284
|
spacing: 4px base */
|
|
@@ -657,8 +1327,44 @@
|
|
|
657
1327
|
--cg-radius-lg: 14px;
|
|
658
1328
|
/* SOURCE: option card */
|
|
659
1329
|
--cg-radius-xl: 16px;
|
|
1330
|
+
/*
|
|
1331
|
+
SOURCE: the corner of every section slab and every card in the design, read on
|
|
1332
|
+
all six canonical frames — home 8204:2440, thegrid 8364:3928, builders
|
|
1333
|
+
6913:8019, teams 7172:7567, enterprise 7037:775, pricing 7785:10051.
|
|
1334
|
+
|
|
1335
|
+
It is the most-used radius in the whole design and the ramp had no name for it.
|
|
1336
|
+
--cg-radius-xl is 16 and --cg-radius-2xl is 24, and both are shipped, so 20 has
|
|
1337
|
+
no free t-shirt name in the right place. Renaming a rung two published
|
|
1338
|
+
consumers already read is a breaking change for a corner.
|
|
1339
|
+
|
|
1340
|
+
So it takes a ROLE name, which the corners group already does once for
|
|
1341
|
+
--cg-radius-pill. The role is exact and worth stating: in these frames 16 is
|
|
1342
|
+
what a CONTROL is rounded to (every button on every frame is 16) and 20 is what
|
|
1343
|
+
a CONTAINER is rounded to. The design draws that distinction consistently, which
|
|
1344
|
+
is more information than a number between two numbers.
|
|
1345
|
+
|
|
1346
|
+
NOT --cg-radius-panel, deliberately: "panel" in this package is the purple
|
|
1347
|
+
surface (--cg-surface-panel, data-cg-surface="panel"), and a radius named after
|
|
1348
|
+
it would read as that surface's corner rather than as every container's. */
|
|
1349
|
+
--cg-radius-container: 20px;
|
|
660
1350
|
/* SOURCE: full-bleed purple panel */
|
|
661
1351
|
--cg-radius-2xl: 24px;
|
|
1352
|
+
/*
|
|
1353
|
+
SOURCE: home 8204:2557 and 8204:2569, the two 92px lime image cards in the hero.
|
|
1354
|
+
|
|
1355
|
+
One frame, two nodes, and it is here rather than recorded-and-skipped for one
|
|
1356
|
+
reason: it extends the t-shirt ramp at the TOP, where there is a free name and
|
|
1357
|
+
no ambiguity, instead of asking for one between two shipped rungs.
|
|
1358
|
+
|
|
1359
|
+
WHAT IS NOT HERE. The brief this work came from expected a rung between
|
|
1360
|
+
--cg-radius-2xl and --cg-radius-pill for a frame nesting 108 / 93 / 80px
|
|
1361
|
+
corners. No radius above 60 and below the pill appears on any of the six frames
|
|
1362
|
+
first-hand. builders draws 60px on three decorative glow rectangles
|
|
1363
|
+
(7172:8460-8462) and thegrid draws 22 / 18 / 15 / 11 / 9 inside a flattened
|
|
1364
|
+
product-UI mock. None of those is a rung; all are recorded here so the next
|
|
1365
|
+
audit does not re-find them. The home frame is only ~10% audited (the Figma
|
|
1366
|
+
quota ran out), so if that expectation has a node behind it, it is there. */
|
|
1367
|
+
--cg-radius-3xl: 32px;
|
|
662
1368
|
--cg-radius-pill: 999px;
|
|
663
1369
|
--cg-border-width: 1px;
|
|
664
1370
|
--cg-border-width-strong: 1.5px;
|
|
@@ -844,6 +1550,12 @@
|
|
|
844
1550
|
--cg-text-primary: #ffffff;
|
|
845
1551
|
--cg-text-secondary: rgba(255, 255, 255, 0.82);
|
|
846
1552
|
--cg-text-muted: rgba(255, 255, 255, 0.66);
|
|
1553
|
+
/*
|
|
1554
|
+
4.75:1 on --cg-surface-panel. See the base note: on this ground nothing dimmer
|
|
1555
|
+
than 0.84 white clears the 4.5:1 floor, so this rung is necessarily brighter
|
|
1556
|
+
than the panel's own --cg-text-secondary (0.82, 4.47:1). The surface is what
|
|
1557
|
+
would have to move to change that, not this value. */
|
|
1558
|
+
--cg-text-muted-strong: rgba(255, 255, 255, 0.86);
|
|
847
1559
|
--cg-text-accent: #ffffff;
|
|
848
1560
|
|
|
849
1561
|
/* ---- semantic-lines ---- */
|
|
@@ -945,6 +1657,14 @@
|
|
|
945
1657
|
--cg-text-primary: #ffffff;
|
|
946
1658
|
--cg-text-secondary: rgba(255, 255, 255, 0.78);
|
|
947
1659
|
--cg-text-muted: rgba(255, 255, 255, 0.56);
|
|
1660
|
+
/*
|
|
1661
|
+
8.02:1 on --cg-surface-code. Ink is the one ground where --cg-text-muted
|
|
1662
|
+
already clears the body floor (5.92:1), so this rung exists there for
|
|
1663
|
+
consistency of meaning rather than to fix a ratio: a component that reaches for
|
|
1664
|
+
text-faint-strong must resolve to something on all three surfaces, and
|
|
1665
|
+
resolving to the base #6a6a6a inside a dark scope would be the CLAUDE.md rule-3
|
|
1666
|
+
failure. It sits between the ink scope's muted (0.56) and its secondary (0.78). */
|
|
1667
|
+
--cg-text-muted-strong: rgba(255, 255, 255, 0.68);
|
|
948
1668
|
--cg-text-accent: var(--cg-secondary);
|
|
949
1669
|
|
|
950
1670
|
/* ---- semantic-surfaces ---- */
|