Added HomePage Components
This commit is contained in:
@@ -22,25 +22,33 @@ const site = import.meta.env.DEV ? await getSite() : null
|
|||||||
const here = site ? [...forEntry(site.diagnostics, collection, id), ...diagnostics] : []
|
const here = site ? [...forEntry(site.diagnostics, collection, id), ...diagnostics] : []
|
||||||
const elsewhere = site ? site.diagnostics.filter((d) => !here.includes(d)) : []
|
const elsewhere = site ? site.diagnostics.filter((d) => !here.includes(d)) : []
|
||||||
const notice = status ? NOTICE[status] : undefined
|
const notice = status ? NOTICE[status] : undefined
|
||||||
|
const label = [status && status !== 'published' ? status : null, here.length || null]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' · ')
|
||||||
---
|
---
|
||||||
|
|
||||||
{site && (here.length || elsewhere.length || notice) && (
|
{site && (here.length || elsewhere.length || notice) && (
|
||||||
<aside class="dev-banner">
|
<details class="dev-banner dark" open>
|
||||||
{notice && <p class="notice">{notice}</p>}
|
<summary class="dev-banner__bar">
|
||||||
{here.length > 0 && (
|
Dev{label && <span class="dev-banner__crumb">{label}</span>}
|
||||||
<ul>
|
</summary>
|
||||||
{here.map((d) => (
|
<div class="dev-banner__body">
|
||||||
<li class={d.level}>
|
{notice && <p class="dev-banner__notice">{notice}</p>}
|
||||||
{d.level === 'error' ? '✖' : '▲'} {d.message}
|
{here.length > 0 && (
|
||||||
</li>
|
<ul class="dev-banner__list" role="list">
|
||||||
))}
|
{here.map((d) => (
|
||||||
</ul>
|
<li class:list={['dev-banner__item', `dev-banner__item--${d.level}`]}>
|
||||||
)}
|
{d.message}
|
||||||
{elsewhere.length > 0 && (
|
</li>
|
||||||
<details>
|
))}
|
||||||
<summary>{elsewhere.length} problem(s) elsewhere</summary>
|
</ul>
|
||||||
<pre>{formatDiagnostics(elsewhere)}</pre>
|
)}
|
||||||
</details>
|
{elsewhere.length > 0 && (
|
||||||
)}
|
<details class="dev-banner__elsewhere">
|
||||||
</aside>
|
<summary class="dev-banner__toggle">{elsewhere.length} elsewhere</summary>
|
||||||
|
<pre class="dev-banner__log">{formatDiagnostics(elsewhere)}</pre>
|
||||||
|
</details>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
)}
|
)}
|
||||||
|
|||||||
31
src/components/home/ActivityStrip.astro
Normal file
31
src/components/home/ActivityStrip.astro
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
import type { Activity } from '@/lib/site/types'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
activity: Activity[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const { activity } = Astro.props
|
||||||
|
const total = activity.length
|
||||||
|
const touched = activity.filter((cell) => cell.touched).length
|
||||||
|
const latest = activity.at(-1)?.touched ?? false
|
||||||
|
const summary = `Updated in ${touched} of ${total} recent release${total === 1 ? '' : 's'}${latest ? ', including the latest' : ''}`
|
||||||
|
---
|
||||||
|
|
||||||
|
{total > 0 && (
|
||||||
|
<p class="activity-strip">
|
||||||
|
<span class="activity-strip__summary">{summary}</span>
|
||||||
|
<span class="activity-strip__cells" aria-hidden="true">
|
||||||
|
{activity.map((cell, i) => (
|
||||||
|
<span
|
||||||
|
class:list={[
|
||||||
|
'activity-strip__cell',
|
||||||
|
cell.touched && 'activity-strip__cell--touched',
|
||||||
|
cell.touched && i === total - 1 && 'activity-strip__cell--latest',
|
||||||
|
]}
|
||||||
|
title={`v${cell.release.version}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
47
src/components/home/Changelog.astro
Normal file
47
src/components/home/Changelog.astro
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
import { CHANGE_TYPES } from '@/lib/constants/changelog'
|
||||||
|
import { toMilitaryDTG } from '@/lib/content/dates'
|
||||||
|
import { trailOf } from '@/lib/site/navigation'
|
||||||
|
import type { Change, Release } from '@/lib/site/types'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
releases: Release[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const LIMIT = 4
|
||||||
|
|
||||||
|
const { releases } = Astro.props
|
||||||
|
|
||||||
|
const labelOf = (kind: Change['kind']) =>
|
||||||
|
CHANGE_TYPES.find((option) => option.value === kind)?.label ?? kind
|
||||||
|
|
||||||
|
const workOf = (change: Change) => (change.article ? trailOf(change.article)[0].title : 'Site')
|
||||||
|
---
|
||||||
|
|
||||||
|
<ol class="changelog" role="list">
|
||||||
|
{releases.map((release) => {
|
||||||
|
const more = release.changes.length - LIMIT
|
||||||
|
return (
|
||||||
|
<li class="changelog__release">
|
||||||
|
<div class="changelog__head">
|
||||||
|
<h3 class="changelog__version">v{release.version}</h3>
|
||||||
|
<time class="changelog__date" datetime={release.date.toISOString()}>
|
||||||
|
{toMilitaryDTG(release.date)}
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
<ul class="changelog__changes" role="list">
|
||||||
|
{release.changes.slice(0, LIMIT).map((change) => (
|
||||||
|
<li class="changelog__change">
|
||||||
|
<span class="changelog__kind">{labelOf(change.kind)}</span>
|
||||||
|
<span class="changelog__work">{workOf(change)}</span>
|
||||||
|
<p class="changelog__text">
|
||||||
|
{change.article ? <a href={change.article.path}>{change.text}</a> : change.text}
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
{more > 0 && <p class="changelog__more">+{more} more</p>}
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</ol>
|
||||||
67
src/components/home/HomeHero.astro
Normal file
67
src/components/home/HomeHero.astro
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
---
|
||||||
|
import { SITE } from '@/lib/constants/site'
|
||||||
|
import { toMilitaryDTG } from '@/lib/content/dates'
|
||||||
|
import type { Release } from '@/lib/site/types'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
works: number
|
||||||
|
entries: number
|
||||||
|
release?: Release
|
||||||
|
}
|
||||||
|
|
||||||
|
const { works, entries, release } = Astro.props
|
||||||
|
---
|
||||||
|
|
||||||
|
<header class="home-hero dark">
|
||||||
|
<div class="home-hero__inner">
|
||||||
|
<p class="home-hero__eyebrow">Index</p>
|
||||||
|
<h1 class="home-hero__title">{SITE.title}</h1>
|
||||||
|
<dl class="home-hero__stats">
|
||||||
|
<div class="home-hero__stat home-hero__stat--about">
|
||||||
|
<dt class="home-hero__label">
|
||||||
|
<span class="home-hero__index" aria-hidden="true">
|
||||||
|
01
|
||||||
|
</span>
|
||||||
|
About
|
||||||
|
</dt>
|
||||||
|
<dd class="home-hero__tagline">{SITE.tagline}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="home-hero__stat">
|
||||||
|
<dt class="home-hero__label">
|
||||||
|
<span class="home-hero__index" aria-hidden="true">
|
||||||
|
02
|
||||||
|
</span>
|
||||||
|
Works
|
||||||
|
</dt>
|
||||||
|
<dd class="home-hero__value">{works}</dd>
|
||||||
|
<dd class="home-hero__note">Top-level</dd>
|
||||||
|
</div>
|
||||||
|
<div class="home-hero__stat">
|
||||||
|
<dt class="home-hero__label">
|
||||||
|
<span class="home-hero__index" aria-hidden="true">
|
||||||
|
03
|
||||||
|
</span>
|
||||||
|
Entries
|
||||||
|
</dt>
|
||||||
|
<dd class="home-hero__value">{entries}</dd>
|
||||||
|
<dd class="home-hero__note">Articles</dd>
|
||||||
|
</div>
|
||||||
|
<div class="home-hero__stat">
|
||||||
|
<dt class="home-hero__label">
|
||||||
|
<span class="home-hero__index" aria-hidden="true">
|
||||||
|
04
|
||||||
|
</span>
|
||||||
|
Latest release
|
||||||
|
</dt>
|
||||||
|
<dd class="home-hero__value">{release ? `v${release.version}` : '—'}</dd>
|
||||||
|
<dd class="home-hero__note">
|
||||||
|
{release ? (
|
||||||
|
<time datetime={release.date.toISOString()}>{toMilitaryDTG(release.date)}</time>
|
||||||
|
) : (
|
||||||
|
'None yet'
|
||||||
|
)}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
21
src/components/home/HomeSection.astro
Normal file
21
src/components/home/HomeSection.astro
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
import { toRoman } from '@/lib/content/numerals'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
index: number
|
||||||
|
title: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const { index, title } = Astro.props
|
||||||
|
const id = `home-section-${index}`
|
||||||
|
---
|
||||||
|
|
||||||
|
<section class="home-section" aria-labelledby={id}>
|
||||||
|
<h2 id={id} class="home-section__heading">
|
||||||
|
<span class="home-section__number" aria-hidden="true">
|
||||||
|
§ {toRoman(index)}
|
||||||
|
</span>
|
||||||
|
{title}
|
||||||
|
</h2>
|
||||||
|
<slot />
|
||||||
|
</section>
|
||||||
44
src/components/home/WorkList.astro
Normal file
44
src/components/home/WorkList.astro
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
import { toRoman } from '@/lib/content/numerals'
|
||||||
|
import { activityOf } from '@/lib/site/activity'
|
||||||
|
import type { Article, Release } from '@/lib/site/types'
|
||||||
|
|
||||||
|
import ActivityStrip from './ActivityStrip.astro'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
works: Article[]
|
||||||
|
releases: Release[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const { works, releases } = Astro.props
|
||||||
|
---
|
||||||
|
|
||||||
|
<ol class="work-list" role="list">
|
||||||
|
{works.map((work, i) => (
|
||||||
|
<li class="work-list__item">
|
||||||
|
<span class="work-list__numeral" aria-hidden="true">
|
||||||
|
{toRoman(i + 1)}
|
||||||
|
</span>
|
||||||
|
<div class="work-list__main">
|
||||||
|
<h3 class="work-list__title">
|
||||||
|
<a href={work.path}>{work.title}</a>
|
||||||
|
</h3>
|
||||||
|
{work.summary.excerpt && <p class="work-list__excerpt">{work.summary.excerpt}</p>}
|
||||||
|
</div>
|
||||||
|
<div class="work-list__meta">
|
||||||
|
{work.children.length > 0 ? (
|
||||||
|
<ul class="work-list__sections" role="list">
|
||||||
|
{work.children.map((section) => (
|
||||||
|
<li>
|
||||||
|
<a href={section.path}>{section.title}</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
) : (
|
||||||
|
<p class="work-list__sections">—</p>
|
||||||
|
)}
|
||||||
|
<ActivityStrip activity={activityOf(work, releases)} />
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ol>
|
||||||
15
src/content/general/articles/awq.mdoc
Normal file
15
src/content/general/articles/awq.mdoc
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
title: Advanced Warhammer Quest
|
||||||
|
status: draft
|
||||||
|
noindex: false
|
||||||
|
stage: stub
|
||||||
|
route:
|
||||||
|
index: 3
|
||||||
|
sequence: false
|
||||||
|
summary:
|
||||||
|
cover:
|
||||||
|
effect: none
|
||||||
|
ratio: original
|
||||||
|
published: 2026-09-30T16:46:00.000Z
|
||||||
|
tags: []
|
||||||
|
---
|
||||||
@@ -5,6 +5,7 @@ status: draft
|
|||||||
noindex: false
|
noindex: false
|
||||||
stage: stub
|
stage: stub
|
||||||
route:
|
route:
|
||||||
|
index: 2
|
||||||
sequence: false
|
sequence: false
|
||||||
summary:
|
summary:
|
||||||
cover:
|
cover:
|
||||||
|
|||||||
@@ -2,12 +2,17 @@
|
|||||||
title: The Crucible
|
title: The Crucible
|
||||||
status: draft
|
status: draft
|
||||||
noindex: false
|
noindex: false
|
||||||
route: {}
|
stage: stub
|
||||||
|
route:
|
||||||
|
index: 1
|
||||||
|
sequence: false
|
||||||
summary:
|
summary:
|
||||||
excerpt: >-
|
excerpt: >-
|
||||||
Where we explore the crucible – a materialistic alchemist Worldbuilding
|
Where we explore the crucible – a materialistic alchemist Worldbuilding
|
||||||
Framework
|
Framework
|
||||||
cover: {}
|
cover:
|
||||||
|
effect: none
|
||||||
|
ratio: original
|
||||||
published: 2026-09-21T12:33:00.000Z
|
published: 2026-09-21T12:33:00.000Z
|
||||||
tags:
|
tags:
|
||||||
- the-crucible
|
- the-crucible
|
||||||
|
|||||||
@@ -5,3 +5,4 @@ noindex: false
|
|||||||
route: {}
|
route: {}
|
||||||
summary: {}
|
summary: {}
|
||||||
---
|
---
|
||||||
|
Set in Blaka{% footnote note="Jdajhdasljkh" /%}.
|
||||||
|
|||||||
@@ -31,12 +31,16 @@ const sequence = sequenceOf(article)
|
|||||||
<div class="body content prose">
|
<div class="body content prose">
|
||||||
<Node node={rendered.tree} />
|
<Node node={rendered.tree} />
|
||||||
</div>
|
</div>
|
||||||
<aside class="margin">
|
{Astro.slots.has('margin') && (
|
||||||
<slot name="margin" />
|
<aside class="margin">
|
||||||
</aside>
|
<slot name="margin" />
|
||||||
<div class="notes content">
|
</aside>
|
||||||
<FootnoteList footnotes={rendered.footnotes} />
|
)}
|
||||||
</div>
|
{rendered.footnotes.length > 0 && (
|
||||||
|
<div class="notes content">
|
||||||
|
<FootnoteList footnotes={rendered.footnotes} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ArticleFooter sequence={sequence} article={article} />
|
<ArticleFooter sequence={sequence} article={article} />
|
||||||
</article>
|
</article>
|
||||||
@@ -61,7 +65,6 @@ const sequence = sequenceOf(article)
|
|||||||
@mixin layout-columns;
|
@mixin layout-columns;
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
row-gap: var(--layout-gutter);
|
|
||||||
align-items: start;
|
align-items: start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -95,6 +98,7 @@ const sequence = sequenceOf(article)
|
|||||||
|
|
||||||
.notes {
|
.notes {
|
||||||
grid-column: body-start / body-end;
|
grid-column: body-start / body-end;
|
||||||
|
margin-top: var(--layout-gutter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
import DevBanner from '@/components/dev/DevBanner.astro'
|
import DevBanner from '@/components/dev/DevBanner.astro'
|
||||||
|
import FootnoteList from '@/components/markdoc/blocks/general/FootnoteList.astro'
|
||||||
import Node from '@/components/markdoc/Node.astro'
|
import Node from '@/components/markdoc/Node.astro'
|
||||||
import SimpleHeader from '@/components/post/Header/SimpleHeader.astro'
|
import SimpleHeader from '@/components/post/Header/SimpleHeader.astro'
|
||||||
import BaseLayout from '@/layouts/BaseLayout.astro'
|
import BaseLayout from '@/layouts/BaseLayout.astro'
|
||||||
@@ -29,6 +30,11 @@ const meta = metaOf(page)
|
|||||||
<div class="body content prose">
|
<div class="body content prose">
|
||||||
<Node node={rendered.tree} />
|
<Node node={rendered.tree} />
|
||||||
</div>
|
</div>
|
||||||
|
{rendered.footnotes.length > 0 && (
|
||||||
|
<div class="notes content">
|
||||||
|
<FootnoteList footnotes={rendered.footnotes} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
@@ -38,15 +44,24 @@ const meta = metaOf(page)
|
|||||||
@mixin layout-wrapper;
|
@mixin layout-wrapper;
|
||||||
@mixin my var(--size-12);
|
@mixin my var(--size-12);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--layout-gutter);
|
||||||
|
|
||||||
@media (--bp-desktop) {
|
@media (--bp-desktop) {
|
||||||
@mixin layout-columns;
|
@mixin layout-columns;
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
|
row-gap: var(--layout-gutter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.body {
|
.body {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body,
|
||||||
|
.notes {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
||||||
@media (--bp-desktop) {
|
@media (--bp-desktop) {
|
||||||
|
|||||||
4
src/lib/constants/site.ts
Normal file
4
src/lib/constants/site.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export const SITE = {
|
||||||
|
title: 'dave-dmg',
|
||||||
|
tagline: 'Essays, frameworks and game tools',
|
||||||
|
} as const
|
||||||
16
src/lib/site/activity.ts
Normal file
16
src/lib/site/activity.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { trailOf } from './navigation'
|
||||||
|
import type { Activity, Article, Release } from './types'
|
||||||
|
|
||||||
|
const LENGTH = 10
|
||||||
|
|
||||||
|
export function activityOf(work: Article, releases: Release[]): Activity[] {
|
||||||
|
return releases
|
||||||
|
.slice(0, LENGTH)
|
||||||
|
.reverse()
|
||||||
|
.map((release) => ({
|
||||||
|
release,
|
||||||
|
touched: release.changes.some(
|
||||||
|
(change) => change.article && trailOf(change.article)[0] === work,
|
||||||
|
),
|
||||||
|
}))
|
||||||
|
}
|
||||||
@@ -61,3 +61,8 @@ export type DirectoryEntry = {
|
|||||||
parent: string | null
|
parent: string | null
|
||||||
depth: number
|
depth: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Activity = {
|
||||||
|
release: Release
|
||||||
|
touched: boolean
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,28 @@
|
|||||||
---
|
---
|
||||||
|
import Changelog from '@/components/home/Changelog.astro'
|
||||||
|
import HomeHero from '@/components/home/HomeHero.astro'
|
||||||
|
import HomeSection from '@/components/home/HomeSection.astro'
|
||||||
|
import WorkList from '@/components/home/WorkList.astro'
|
||||||
import BaseLayout from '@/layouts/BaseLayout.astro'
|
import BaseLayout from '@/layouts/BaseLayout.astro'
|
||||||
|
import { SITE } from '@/lib/constants/site'
|
||||||
|
import { getSite } from '@/lib/site'
|
||||||
|
|
||||||
|
const site = await getSite()
|
||||||
|
const entries = [...site.articles.values()].filter((article) => article.listed).length
|
||||||
|
const releases = site.releases.slice(0, 3)
|
||||||
|
const hasChangelog = releases.length > 0
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title="Genesis — Crucible" description="World generation, cosmology first.">
|
<BaseLayout title={SITE.title} description={SITE.tagline}>
|
||||||
<h1>I AM TEST</h1>
|
<HomeHero works={site.roots.length} entries={entries} release={site.releases[0]} />
|
||||||
<p>ARE YOU TEST?</p>
|
{hasChangelog && (
|
||||||
|
<HomeSection index={1} title="Changelog">
|
||||||
|
<Changelog releases={releases} />
|
||||||
|
</HomeSection>
|
||||||
|
)}
|
||||||
|
{site.roots.length > 0 && (
|
||||||
|
<HomeSection index={hasChangelog ? 2 : 1} title="Works">
|
||||||
|
<WorkList works={site.roots} releases={site.releases} />
|
||||||
|
</HomeSection>
|
||||||
|
)}
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|||||||
122
src/styles/components/dev/dev-banner.css
Normal file
122
src/styles/components/dev/dev-banner.css
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
@layer components.blocks {
|
||||||
|
.dev-banner {
|
||||||
|
@mixin border-t var(--border-boundary), solid, var(--color-tertiary);
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
z-index: 2;
|
||||||
|
bottom: var(--size-6);
|
||||||
|
left: var(--size-6);
|
||||||
|
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
max-width: min(28rem, 100% - 2 * var(--size-6));
|
||||||
|
max-height: 50dvh;
|
||||||
|
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
|
||||||
|
background-color: var(--color-surface-base);
|
||||||
|
box-shadow: var(--shadow-raised);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-banner__bar {
|
||||||
|
@mixin typo_eyebrow;
|
||||||
|
@mixin py var(--size-4);
|
||||||
|
@mixin px var(--size-6);
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: var(--font-weight-emphasis);
|
||||||
|
color: var(--color-tertiary);
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
&::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
@mixin focus-ring;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-banner__crumb {
|
||||||
|
&::before {
|
||||||
|
content: "⟫" / "";
|
||||||
|
margin-inline: var(--size-2);
|
||||||
|
font-family: var(--font-body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-banner__body {
|
||||||
|
@mixin px var(--size-6);
|
||||||
|
@mixin pb var(--size-6);
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
gap: var(--size-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-banner__notice {
|
||||||
|
@mixin typo_eyebrow;
|
||||||
|
@mixin ma var(--size-0);
|
||||||
|
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-banner__list {
|
||||||
|
@mixin ma var(--size-0);
|
||||||
|
@mixin pa var(--size-0);
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
gap: var(--size-2);
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-banner__item {
|
||||||
|
@mixin typo_code;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto minmax(0, 1fr);
|
||||||
|
gap: var(--size-4);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
font-family: var(--font-body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-banner__item--error {
|
||||||
|
&::before {
|
||||||
|
content: "✖" / "";
|
||||||
|
color: var(--color-state-error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-banner__item--warning {
|
||||||
|
&::before {
|
||||||
|
content: "▲" / "";
|
||||||
|
color: var(--color-state-warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-banner__elsewhere {
|
||||||
|
@mixin pt var(--size-4);
|
||||||
|
@mixin border-t var(--border-hairline), solid, var(--color-border-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-banner__toggle {
|
||||||
|
@mixin typo_eyebrow;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--color-text-quiet);
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
@mixin focus-ring;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-banner__log {
|
||||||
|
@mixin typo_code;
|
||||||
|
@mixin mt var(--size-4);
|
||||||
|
@mixin mb var(--size-0);
|
||||||
|
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
src/styles/components/dev/index.css
Normal file
1
src/styles/components/dev/index.css
Normal file
@@ -0,0 +1 @@
|
|||||||
|
@import url("dev-banner.css");
|
||||||
30
src/styles/components/home/activity-strip.css
Normal file
30
src/styles/components/home/activity-strip.css
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
@layer components.blocks {
|
||||||
|
.activity-strip {
|
||||||
|
@mixin ma var(--size-0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-strip__summary {
|
||||||
|
@mixin visually-hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-strip__cells {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--size-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-strip__cell {
|
||||||
|
aspect-ratio: 1;
|
||||||
|
width: var(--size-5);
|
||||||
|
border: var(--border-hairline) solid var(--color-border-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-strip__cell--touched {
|
||||||
|
border-color: var(--color-surface-inverse);
|
||||||
|
background-color: var(--color-surface-inverse);
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-strip__cell--latest {
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
background-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
121
src/styles/components/home/changelog.css
Normal file
121
src/styles/components/home/changelog.css
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
@layer components.blocks {
|
||||||
|
.changelog {
|
||||||
|
@mixin ma var(--size-0);
|
||||||
|
@mixin pa var(--size-0);
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
gap: var(--border-card);
|
||||||
|
|
||||||
|
border: var(--border-card) solid var(--color-border-strong);
|
||||||
|
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
background-color: var(--color-border-strong);
|
||||||
|
|
||||||
|
@media (--bp-tablet-up) {
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.changelog__release {
|
||||||
|
@mixin pa var(--size-8);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--size-6);
|
||||||
|
background-color: var(--color-surface-elevated-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.changelog__head {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--size-4);
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.changelog__version {
|
||||||
|
@mixin ma var(--size-0);
|
||||||
|
|
||||||
|
font-family: var(--font-header);
|
||||||
|
font-size: var(--type-6);
|
||||||
|
font-weight: var(--font-weight-heading);
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.changelog__date {
|
||||||
|
@mixin typo_tag;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "⟫" / "";
|
||||||
|
margin-inline-end: var(--size-2);
|
||||||
|
font-family: var(--font-body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.changelog__changes {
|
||||||
|
@mixin ma var(--size-0);
|
||||||
|
@mixin pa var(--size-0);
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
gap: var(--size-6);
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.changelog__change {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto minmax(0, 1fr);
|
||||||
|
gap: var(--size-2) var(--size-4);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.changelog__kind {
|
||||||
|
@mixin typo_tag;
|
||||||
|
@mixin py var(--size-1);
|
||||||
|
@mixin px var(--size-3);
|
||||||
|
|
||||||
|
justify-self: start;
|
||||||
|
color: var(--color-text-inverse);
|
||||||
|
background-color: var(--color-surface-inverse);
|
||||||
|
}
|
||||||
|
|
||||||
|
.changelog__work {
|
||||||
|
@mixin typo_eyebrow;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
color: var(--color-text-quiet);
|
||||||
|
text-align: end;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.changelog__text {
|
||||||
|
@mixin typo_body-small;
|
||||||
|
@mixin ma var(--size-0);
|
||||||
|
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
|
||||||
|
& a {
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-color: var(--color-border-normal);
|
||||||
|
text-underline-offset: 0.2em;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
@mixin focus-ring;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.changelog__more {
|
||||||
|
@mixin typo_eyebrow;
|
||||||
|
@mixin mt auto;
|
||||||
|
@mixin mb var(--size-0);
|
||||||
|
|
||||||
|
color: var(--color-text-quiet);
|
||||||
|
}
|
||||||
|
}
|
||||||
114
src/styles/components/home/home-hero.css
Normal file
114
src/styles/components/home/home-hero.css
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
@layer components.blocks {
|
||||||
|
.home-hero {
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
background-color: var(--color-surface-base);
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-hero__inner {
|
||||||
|
@mixin layout-wrapper;
|
||||||
|
@mixin py var(--size-12);
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
gap: var(--size-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-hero__eyebrow {
|
||||||
|
@mixin typo_eyebrow;
|
||||||
|
@mixin ma var(--size-0);
|
||||||
|
@mixin pb var(--size-4);
|
||||||
|
@mixin border-b var(--border-hairline), solid, var(--color-border-subtle);
|
||||||
|
|
||||||
|
color: var(--color-text-quiet);
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-hero__title {
|
||||||
|
@mixin ma var(--size-0);
|
||||||
|
|
||||||
|
font-family: var(--font-display);
|
||||||
|
font-size: var(--type-slab);
|
||||||
|
font-weight: var(--font-weight-display);
|
||||||
|
line-height: 0.9;
|
||||||
|
text-shadow: var(--size-2) var(--size-2) 0 var(--color-primary);
|
||||||
|
letter-spacing: var(--tracking-display);
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-hero__stats {
|
||||||
|
@mixin ma var(--size-0);
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: var(--border-hairline);
|
||||||
|
|
||||||
|
border: var(--border-hairline) solid var(--color-border-subtle);
|
||||||
|
|
||||||
|
background-color: var(--color-border-subtle);
|
||||||
|
|
||||||
|
@media (--bp-tablet-up) {
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-hero__stat {
|
||||||
|
@mixin pa var(--size-8);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--size-3);
|
||||||
|
|
||||||
|
min-height: var(--size-16);
|
||||||
|
|
||||||
|
background-color: var(--color-surface-elevated-1);
|
||||||
|
|
||||||
|
& dd {
|
||||||
|
@mixin ma var(--size-0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-hero__stat--about {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
color: var(--color-text-on-accent);
|
||||||
|
background-color: var(--color-primary);
|
||||||
|
|
||||||
|
@media (--bp-tablet-up) {
|
||||||
|
grid-column: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-hero__label {
|
||||||
|
@mixin typo_eyebrow;
|
||||||
|
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
|
||||||
|
.home-hero__stat--about & {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-hero__index {
|
||||||
|
&::after {
|
||||||
|
content: "⟫" / "";
|
||||||
|
margin-inline: var(--size-2);
|
||||||
|
font-family: var(--font-body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-hero__tagline {
|
||||||
|
@mixin typo_heading-3;
|
||||||
|
@mixin mt auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-hero__value {
|
||||||
|
@mixin mt auto;
|
||||||
|
|
||||||
|
font-family: var(--font-header);
|
||||||
|
font-size: var(--type-7);
|
||||||
|
font-weight: var(--font-weight-heading);
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-hero__note {
|
||||||
|
@mixin typo_eyebrow;
|
||||||
|
|
||||||
|
color: var(--color-text-quiet);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/styles/components/home/home-section.css
Normal file
25
src/styles/components/home/home-section.css
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
@layer components.blocks {
|
||||||
|
.home-section {
|
||||||
|
@mixin layout-wrapper;
|
||||||
|
@mixin my var(--size-14);
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-section__heading {
|
||||||
|
@mixin typo_heading-2;
|
||||||
|
@mixin mt var(--size-0);
|
||||||
|
@mixin mb var(--size-8);
|
||||||
|
@mixin pt var(--size-6);
|
||||||
|
@mixin border-t var(--border-boundary), solid, var(--color-border-strong);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
gap: var(--size-6);
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-section__number {
|
||||||
|
@mixin typo_eyebrow;
|
||||||
|
|
||||||
|
font-weight: var(--font-weight-emphasis);
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/styles/components/home/index.css
Normal file
5
src/styles/components/home/index.css
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
@import url("home-hero.css");
|
||||||
|
@import url("home-section.css");
|
||||||
|
@import url("changelog.css");
|
||||||
|
@import url("work-list.css");
|
||||||
|
@import url("activity-strip.css");
|
||||||
119
src/styles/components/home/work-list.css
Normal file
119
src/styles/components/home/work-list.css
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
@layer components.blocks {
|
||||||
|
.work-list {
|
||||||
|
@mixin ma var(--size-0);
|
||||||
|
@mixin pa var(--size-0);
|
||||||
|
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.work-list__item {
|
||||||
|
@mixin py var(--size-8);
|
||||||
|
@mixin border-b var(--border-hairline), solid, var(--color-border-strong);
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas:
|
||||||
|
"numeral main"
|
||||||
|
"numeral meta";
|
||||||
|
grid-template-columns: var(--size-14) minmax(0, 1fr);
|
||||||
|
gap: var(--size-6) var(--size-10);
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
@media (--bp-tablet-up) {
|
||||||
|
grid-template-areas: "numeral main meta";
|
||||||
|
grid-template-columns: var(--size-16) minmax(0, 1fr) auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.work-list__numeral {
|
||||||
|
grid-area: numeral;
|
||||||
|
align-self: start;
|
||||||
|
|
||||||
|
font-family: var(--font-header);
|
||||||
|
font-size: var(--type-9);
|
||||||
|
font-weight: var(--font-weight-heading);
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.work-list__main {
|
||||||
|
grid-area: main;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.work-list__title {
|
||||||
|
@mixin ma var(--size-0);
|
||||||
|
|
||||||
|
font-family: var(--font-header);
|
||||||
|
font-size: clamp(var(--type-5), 3vw, var(--type-8));
|
||||||
|
font-weight: var(--font-weight-heading);
|
||||||
|
line-height: 1;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: var(--tracking-8);
|
||||||
|
|
||||||
|
& a {
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-color: var(--color-primary);
|
||||||
|
text-decoration-thickness: var(--border-card);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
@mixin focus-ring;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.work-list__excerpt {
|
||||||
|
@mixin typo_body-small;
|
||||||
|
@mixin mt var(--size-3);
|
||||||
|
@mixin mb var(--size-0);
|
||||||
|
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.work-list__meta {
|
||||||
|
display: flex;
|
||||||
|
grid-area: meta;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--size-4);
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
@media (--bp-tablet-up) {
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.work-list__sections {
|
||||||
|
@mixin typo_eyebrow;
|
||||||
|
@mixin ma var(--size-0);
|
||||||
|
@mixin pa var(--size-0);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: var(--size-2) var(--size-3);
|
||||||
|
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
& li + li {
|
||||||
|
&::before {
|
||||||
|
content: "·" / "";
|
||||||
|
margin-inline-end: var(--size-3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& a {
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
@mixin focus-ring;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (--bp-tablet-up) {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,3 +2,5 @@
|
|||||||
@import url("blocks/index.css");
|
@import url("blocks/index.css");
|
||||||
@import url("post/index.css");
|
@import url("post/index.css");
|
||||||
@import url("site/index.css");
|
@import url("site/index.css");
|
||||||
|
@import url("home/index.css");
|
||||||
|
@import url("dev/index.css");
|
||||||
|
|||||||
Reference in New Issue
Block a user