Shells and frames

The viewer shell, the sheet frame and the plate: the frames every hub page and figure sits in, taken apart and adjustable.

Updated

Every page of the hub sits in the viewer shell: a sidebar, a 52px toolbar, a stage and an index panel that slides over it. A slide on the stage sits in the fixed sheet, which scales a 1600 by 900 artifact to fit and draws the deck's edge grid on it. Every live figure on these docs pages sits in a plate. This page takes the three frames apart.

The viewer shell

ViewerShell is a fixed frame over the whole viewport, so the plate draws a model of it at a window size: every region stands where the shell's stylesheets put it and draws only the edges the junction table gives it. The stage holds the live parts: the fixed Sheet with its SheetFrame in slide mode, the GridView in grid mode, and the flow Sheet holding a book page in book mode.

Window sets the model's size, and Stage switches the mode as the toolbar's segmented control does. List density picks the sidebar's two widths, List and Index open and close the sidebar and the index panel, and Present takes the chrome away. At 900 pixels and under the list starts closed and floats over the stage above a scrim when it opens, and the sheet pads 12 pixels and sits under the toolbar. A click on either half of the slide pages it.

RegionSizeThe edge it owns
Sidebar208px in outline density, 256px in thumbnail density; at 900px and under an overlay min(84vw, 300px) wideits right edge
Sidebar head52px: the mark, the title and the density toggleits bottom edge
Filter row40pxits bottom edge
List rows28px in outline density with one rail in the row role down each group; 44px with a 64 by 36 capture in thumbnail density; group headers 24px with 16px abovenone
Toolbar52px, its two groups 8px apart insideits bottom edge
Stagethe main region between the toolbar and the progress linenone
Index panel460px from the toolbar's rule to the main region's foot, over the progress line, on a 28 percent ink scrimits left edge
Progress line2px: a --pt-hair-soft track with an ink fillnone

The shell counts as narrow at 900 pixels and under, and on a phone on its side, which matches (pointer: coarse) and (max-height: 500px). Present closes the sidebar's column and removes the toolbar and the progress line, so the stage is the window.

JunctionOwnerThe other side
Sidebar and stagethe sidebar's right edgethe main region draws no left edge
Toolbar and stagethe toolbar's bottom edgethe stage and the index panel draw no top edge
Index panel and stagethe panel's left edgea fixed sheet's ring runs under the panel
Sidebar head and toolbareach its own side of the seamthe two bottom rules meet at the sidebar's edge and never overlap

Import and props

import { ViewerShell } from '@/components/viewer/ViewerShell';

<ViewerShell
  count='52 slides'
  id='deck'
  keys='paged'
  mark='gt'
  modes={['slide', 'grid', 'book']}
  sections={sections}
  surfaces={surfaces}
  thumb='shot'
  title='Deck'
>
  <Sheet variant='fixed'>{slides}</Sheet>
</ViewerShell>;
PropTypeWhat it does
idstringThe storage namespace for the saved mode, and body[data-shell].
title, mark, countstring, 'gt' or 'pt', stringThe sidebar head and the toolbar's brand; the count is already worded.
sectionsShellSection[]The route's items, grouped; the sidebar, the grid and the count read them.
modesShellMode[]The modes the route offers, in the toolbar's fixed order; the first is the default.
keys'paged', 'flow' or a function of the modePaged routes take the arrows and Space; flow routes let them scroll.
thumb'shot' or 'row'Captured frames or frameless rows in the list and the grid.
surfacesSurfaceSetThe registry the index panel opens on.
toolbarSlotnodeThe route's own controls, first in the toolbar's right group.
childrennodeThe stage content: a Sheet, and a BookView while the mode is book.

Children read the shell's state with usePtShell(): the mode, the active item, the count, narrow and present, and the setters.

The fixed sheet

The fixed sheet is the edge of a fixed-size artifact: a slide, a site exhibit at 1440 pixels, a pane of the compare rig. The plate mounts the live Sheet and SheetFrame on a stage of the size the sliders give, inside the two contexts ViewerShell would publish, and prints what fitSheet() returns. Previous and Next page it, and so does a click on either half of the sheet.

fitSheet() scales the 1600 by 900 stage to the space left after the pad and centers it, then places the sheet's border box one pixel out, so the border sits around the scaled content and never over it. The ring is drawn as a mat with no shadow: a 1px --pt-hair border on the sheet, a 1px gap of paper, and a 1px --pt-hair-soft outline. The stage around it paints --pt-plate, the letterbox. At 900 pixels and under the pad drops to 12 pixels and the sheet sits under the toolbar, and Present removes the ring, the plate and the pad.

PartValue
Stage1600 by 900 for a slide, 1440 by 900 for a site exhibit, scaled by fitSheet()
Pad28px, 12px at 900px and under, 0 while presenting
Ringa 1px --pt-hair border, a 1px paper gap, a 1px --pt-hair-soft outline
Rails1px --pt-hair, 56px in from the left and right edges
Rules1px --pt-hair, 56px in from the top and bottom edges
Crosses11 by 11 at --pt-cross, centered on the four meeting points
Wordmarkthe GT mark at 28 by 18, 72px from the left and 18px from the bottom, in titanium
Counter13px tabular figures in titanium, 72px from the right and 22px from the bottom
Paginga click on either half of the sheet, or a 40px swipe; chevrons show at the edges on hover

Import and props

import { Sheet, fitSheet } from '@/components/viewer/Sheet';

<Sheet variant='fixed' w={1600} h={900}>
  {slides}
</Sheet>;
<Sheet variant='flow' scrollRef={region}>
  {book}
</Sheet>;
const fit = fitSheet({ aw: 1232, ah: 846, w: 1600, h: 900, pad: 28 });
PropTypeDefaultWhat it does
variant'fixed' or 'flow'requiredThe scaled artifact on the plate, or a reading page in the book column.
w, hnumber1600, 900The fixed stage's size before scaling.
framebooleantrueMounts SheetFrame: the rails, rules, crosses, wordmark and counter. Site exhibits turn it off.
fit'contain' or 'height''contain'height fills the stage's height and pans sideways.
captionnode or falsethe item and its widthThe 24px line under a frameless sheet.
itemKeystringnoneKeys the children by item, so the leaving slide fades while the next one rises in.
scrollRefrefnoneThe flow sheet's scroll region, for the progress line and scroll spies.

SheetFrame takes wordmark and counter, both on by default; thumbnail and page clones turn both off to carry the grid alone.

The reading column

The flow sheet holds the deck's book column: 1280 pixels at most, padded by --pt-col-pad-x at each side, --pt-title-clear over the title and 120 pixels under the last block. The column draws no border, no ground and no mat, and every structural rule of a book runs across the whole stage. The book mode of the plate above draws a book page as a skeleton at these values.

TokenValue900px and under
--pt-col-pad-x56px16px
--pt-title-clear44px24px
--pt-d1 (the title)44px on 1.04, -0.025em32px
--pt-head-gap14px from the title to the lead14px
--pt-head-col-gap, --pt-head-panel56px, then a 304px panel beside the leadthe panel stacks under the lead below 880px of head width
--pt-row26.35px, one lead line, the panel's row pitchthe same
--pt-head-rule-pad26px to the mast's rule26px
--pt-sec-over48px from the block above to the band40px
--pt-band-h42px, both rules included42px
--pt-sec-pad30px from a divider's rule to its h230px

The plate

Every live demo on these pages is a Plate: a card with the stage on top and the controls row under it, one hairline between them. This plate's own props are its controls, so the stage, the corner tag and the ground change as they are set.

The stage follows the docs theme unless night pins it to ink, which the GPU fields need because their palettes are tuned to an ink ground. A fit stage takes its content's height, padded 52 pixels over so the corner tag clears it. A lazy plate mounts its children only when it comes within 180 pixels of the viewport, and a moves plate says in its controls row when reduced motion holds it still.

PartValue
Frame1px --pt-hair on the 6px card corner, 24px over the plate and 12px under it
Stage300px tall by default, phoneHeight under 640px; fit pads 52px, 20px and 24px
Tag10px from the stage's corner, a chip on the stage's ground, in mono at the group size
Controls rowa --pt-hair rule over it, 12px and 18px gaps, 12px, 14px and 14px of padding

Import and props

import { Choice, Range, Toggle } from '@/components/docs/demos/controls';
import Plate from '@/components/docs/demos/Plate';

<Plate controls={<Range label='Gap' max={7} min={1} step={0.5} value={gap} onChange={setGap} />} height={280} moves tag='<DoubledLine />'>
  {drawing}
</Plate>;
PropTypeDefaultWhat it does
tagstringrequiredThe entry point's name in the stage's corner.
labelstringrequiredWhat the plate shows, for assistive technology.
controlsnodenoneThe controls row: Choice, Range, Toggle and Action from controls.tsx.
height, phoneHeightnumber300, the heightThe stage's height at desktop width and under 640px.
nightbooleanfalseAn ink stage in both themes.
fitbooleanfalseThe stage takes its content's height.
barebooleanfalseNo corner tag, for a drawing that fills the stage.
lazybooleanfalseMounts the children only once the plate nears the viewport.
movesbooleanfalseUnder reduced motion, the controls row says the plate shows its still.
stageClassNamestringnoneA class on the stage, for the demo's own layout.

The plates on the page layout pages lay a page part out at its own width and scale it into the stage, so a 1440 pixel page reads inside the docs column without overflowing it. The media queries inside a model still follow the real viewport, so each model reads its breakpoint values from the width it is given.

On this page