/* The object needs three things: a square box, a mono face on the canvas (the atlas bakes from it), and a `color` on the element (the print is drawn in whatever ink resolves there). */ /* Dark is the default ground. The blue is lifted a step from the paper value so thin glyphs hold up against a near-black ground. */ :root { /* The renderer reads whatever `color` resolves to on the element. */ --mark: #4457ff; --ground: #0a0807; --font-mono: "IBM Plex Mono", ui-monospace, monospace; --page-padding: 1.5rem; } :root[data-theme="light"] { --mark: #2740de; --ground: #ffffff; } * { box-sizing: border-box; } body { margin: 0; min-height: 100svh; background: var(--ground); color: var(--mark); font-family: var(--font-mono); -webkit-font-smoothing: antialiased; } .stage { display: flex; align-items: center; justify-content: center; /* The frame is in flow above this until the wide breakpoint, where it goes fixed and the stage gets the viewport back to itself. */ min-height: calc(100svh - 12rem); padding: 24px; } @media screen and (min-width: 53em) { .stage { min-height: 100svh; } } /* The demo frame. It prints in the mark's ink rather than blending against the ground, since the page already sets its two colours and the frame is just more text in that ink. Fixed and full height from the wide breakpoint up, so the links sit in corners the mark's box never reaches. */ .frame { position: relative; z-index: 1000; display: grid; padding: 1rem var(--page-padding) 0; grid-row-gap: 0.5rem; grid-column-gap: 1rem; grid-template-columns: min-content min-content min-content 1fr; /* The switch takes a row of its own here: columns 1 to 3 are sized by the link row, which leaves too little of the fourth for it to sit beside the title without overflowing. */ grid-template-areas: "title title title title" "back archive github ..." "credits credits credits credits" "tags tags tags tags" "theme theme theme theme"; justify-items: start; align-items: center; /* The header covers the stage on the wide breakpoint, so only its own links take the pointer. */ pointer-events: none; color: var(--mark); font-family: var(--font-mono); font-size: 0.8125rem; letter-spacing: 0.04em; text-transform: uppercase; } .frame a, .frame button { pointer-events: auto; } .frame a { color: var(--mark); text-decoration: none; white-space: nowrap; } .frame a:hover { text-decoration: underline; } .frame a:focus-visible { outline: 2px solid var(--mark); outline-offset: 2px; } .frame__title { grid-area: title; margin: 0; font-size: 0.8125rem; font-weight: 400; letter-spacing: 0.04em; } .frame__back { grid-area: back; } .frame__archive { grid-area: archive; } .frame__github { grid-area: github; } .frame__credits { grid-area: credits; } .frame__tags { grid-area: tags; display: flex; flex-wrap: wrap; gap: 1rem; } /* Four corners and a bottom rail: the mark keeps the middle. */ @media screen and (min-width: 53em) { .frame { position: fixed; top: 0; left: 0; width: 100%; height: 100%; padding: var(--page-padding); grid-column-gap: 2rem; grid-template-columns: auto auto auto auto 1fr auto; grid-template-areas: "title back archive github ... credits" "tags tags tags tags ... theme"; align-content: space-between; } .frame__tags { gap: 2rem; } .frame__credits { justify-self: end; } } /* Borrows the mark's ink and the page's face, so both themes style it for free. */ .theme-switch { grid-area: theme; justify-self: end; display: flex; gap: 4px; padding: 4px; border: 1px solid color-mix(in srgb, var(--mark) 35%, transparent); border-radius: 999px; } .theme-switch button { padding: 6px 14px; border: 0; border-radius: 999px; background: none; color: var(--mark); font-family: var(--font-mono); font-size: 13px; letter-spacing: 0.04em; cursor: pointer; } .theme-switch button[aria-pressed="true"] { background: var(--mark); color: var(--ground); cursor: default; } .theme-switch button:focus-visible { outline: 2px solid var(--mark); outline-offset: 2px; } /* `aspect-ratio` reserves the box from first paint, so the canvas is never a layout shift. */ ascii-logo { position: relative; display: block; color: var(--mark); /* Off the short side, so the square fits whichever way the window is turned. */ width: min(84vmin, 900px); aspect-ratio: 1; cursor: grab; touch-action: pan-y; user-select: none; } ascii-logo.is-grabbing { cursor: grabbing; } ascii-logo [data-logo-canvas] { position: absolute; inset: 0; width: 100%; height: 100%; /* Read back by the atlas builder: the sheet is rasterized from this exact face and weight. */ font-family: var(--font-mono); font-weight: 400; opacity: 1; transition: opacity 500ms ease-out; } ascii-logo .is-hidden { opacity: 0; } @media (prefers-reduced-motion: reduce) { ascii-logo [data-logo-canvas] { transition: none; } }