Every list in a post rendered its bullet alone on one line and the item's text on the next. The panel's editor wraps each item's text in a <p>, and @pi/ui's reset puts the bullet inside the line: a block after an inline marker falls below it, paragraph margins and all. Bullets now sit outside, with the paragraph inside an item unspaced. Images in the text — attached in the panel or written in by URL through the API — are never wider than the column and sit apart from the prose, with a caption under a figure. Inline code and code blocks get a tinted surface; quotes keep the library's own quotation marks. The mock's posts carry such a body, so the typography can be reviewed without writing one in the panel. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
362 lines
7.7 KiB
SCSS
362 lines
7.7 KiB
SCSS
// 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 `<img>` 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 `<p>`:
|
|
// 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;
|
|
}
|