// The layout every page reached by its address shares — a content page, a // project, a member — so moving between them reads as one site. // // A partial each body `@use`s rather than a global sheet: component styles are // scoped, and each body gets its own scoped copy of these rules. // The top of the page is the landing's hero, minus the stats and buttons: the // same left edge as the logo, the same heading weight. .content-hero { padding-block: 5rem 2.5rem; @media (min-width: 768px) { padding-block: 7rem 3.5rem; } } .content-hero__title { margin: 0; max-width: 22ch; font-size: clamp(2.25rem, 5.5vw, 3.5rem); line-height: 1.08; letter-spacing: -0.02em; } .content-hero__lead { max-width: 46ch; margin: 1.5rem 0 0; } // Reading column and a side column, on the landing's own grid width. The // column keeps prose to a readable measure; the side keeps the page from // ending in a dead end. .content-page__layout { display: grid; gap: 3rem; padding-bottom: 5rem; @media (min-width: 1024px) { grid-template-columns: minmax(0, 46rem) minmax(16rem, 20rem); justify-content: space-between; gap: 4rem; padding-bottom: 7rem; } } .content-page__media { margin: 0 0 2.5rem; overflow: hidden; border-radius: var(--pi-theme-card-radius, 1.5rem); } // The `` belongs to `app-api-image`, so a scoped rule cannot reach it. .content-page__media ::ng-deep img { display: block; width: 100%; height: auto; } .content-page__body { font-size: 1.0625rem; // The body is HTML from the panel's editor, not this component's template, // so it carries none of the scoping attributes: only `::ng-deep` reaches it. // Confined to this container, it styles nothing else on the page. ::ng-deep { // Bold, like every heading on the landing — a light heading here was the // one thing that made the page read as another site. h2 { margin: 3rem 0 1rem; font-size: clamp(1.5rem, 3vw, 1.875rem); font-weight: 700; line-height: 1.2; letter-spacing: -0.015em; } h3 { margin: 2rem 0 0.75rem; font-size: 1.25rem; font-weight: 700; } // One rhythm for both: left alone, the library's sheet sets paragraphs // tighter than list items and the body reads in two different spacings. p, li { line-height: 1.7; } p { margin: 0 0 1rem; } // The bullet outside the text, where a list's bullet goes. `@pi/ui`'s // reset puts it inside, and the editor wraps each item's text in a `

`: // together the bullet sat alone on one line and the text, a block, fell to // the next — with a paragraph's margins on top. ul, ol { margin: 0 0 1.5rem; padding-left: 1.5rem; } li { margin: 0 0 0.5rem; padding-left: 0.25rem; list-style-position: outside; &::marker { color: var(--pi-color-primary); } > p { margin: 0; } } strong { font-weight: 700; } a { color: var(--pi-color-primary); } code { padding: 0.1em 0.35em; border-radius: 0.375rem; background: color-mix(in srgb, var(--pi-color-primary) 10%, transparent); font-size: 0.9em; } pre { margin: 0 0 1.5rem; padding: 1rem 1.25rem; overflow-x: auto; border-radius: var(--pi-border-radius-medium, 0.75rem); background: color-mix(in srgb, var(--pi-color-primary) 7%, transparent); line-height: 1.55; code { padding: 0; background: none; } } // `@pi/ui` already frames a quote with its own quotation marks; only the // spacing is this page's business. blockquote { margin: 0 0 1.5rem; p:last-child { margin-bottom: 0; } } hr { margin: 2.5rem 0; border: 0; border-top: 1px solid color-mix(in srgb, currentColor 15%, transparent); } // Images in the text — attached in the panel's editor, or by URL through // the API. Never wider than the column, and set apart from the prose. img { display: block; max-width: 100%; height: auto; margin: 2rem 0; border-radius: var(--pi-border-radius-medium, 0.75rem); } figure { margin: 2rem 0; img { margin: 0; } } figcaption { margin-top: 0.5rem; font-size: 0.875rem; color: color-mix(in srgb, currentColor 68%, transparent); } > :first-child { margin-top: 0; } } } .content-page__aside { display: flex; flex-direction: column; gap: 2rem; // Stays in view beside a long page, under the sticky header. @media (min-width: 1024px) { position: sticky; top: 6rem; align-self: start; } } .content-page__related { display: grid; gap: 1rem; margin: 0; padding: 0; list-style: none; } // `landing-card` is the landing's card surface and hover, drawn on a link // rather than on `pi-card`, which brings no padding or radius of its own here. .content-page__card, .content-page__cta { display: block; padding: 1.25rem 1.5rem; border-radius: var(--pi-theme-card-radius, 1.5rem); box-shadow: var(--pi-theme-card-shadow, none); } .content-page__card { color: inherit; text-decoration: none; } .content-page__card-title { display: block; font-size: 1.0625rem; font-weight: 700; } .content-page__card-text { display: -webkit-box; margin-top: 0.375rem; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; font-size: 0.875rem; line-height: 1.5; // Relative to the card's own text colour, so it holds up in dark mode too, // where the grey token is nearly the background. color: color-mix(in srgb, currentColor 68%, transparent); } .content-page__cta { padding: 1.5rem; // The call to action is not a link as a whole, so it does not lift. &:hover { transform: none; } p { margin: 0.5rem 0 1.25rem; line-height: 1.6; } } .content-page__cta-title { margin: 0; font-size: 1.375rem; font-weight: 700; } // Hero actions — a demo, a repository — and the chips under them. .content-hero__actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 2rem; a { text-decoration: none; } } .content-hero__chips { display: flex; flex-wrap: wrap; gap: 0.375rem; margin: 1.5rem 0 0; padding: 0; list-style: none; } // A section inside the reading column, under its own bold heading. .content-page__section { margin-top: 3.5rem; > h2 { margin: 0 0 1.25rem; font-size: clamp(1.5rem, 3vw, 1.875rem); font-weight: 700; line-height: 1.2; letter-spacing: -0.015em; } } .content-page__main > :first-child { margin-top: 0; } // A card in the side column that holds facts rather than a link. .content-page__panel { display: block; padding: 1.25rem 1.5rem; border-radius: var(--pi-theme-card-radius, 1.5rem); box-shadow: var(--pi-theme-card-shadow, none); &:hover { transform: none; } h2 { margin: 0 0 1rem; font-size: 1.0625rem; font-weight: 700; } } // Label and value, one pair a row. .content-page__facts { display: grid; gap: 0.875rem; margin: 0; dt { font-size: 0.75rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: color-mix(in srgb, currentColor 60%, transparent); } dd { margin: 0.125rem 0 0; } a { color: var(--pi-color-primary); } } .content-page__muted { color: color-mix(in srgb, currentColor 68%, transparent); } // Lists this layout spaces with `gap`. `@pi/ui`'s reset adds a top margin to // every `li + li`, which suits a list of prose and staggers everything else. .content-hero__chips, .content-page__related { > li { margin: 0; } } .content-hero__chips { align-items: center; }