Added HomePage Components

This commit is contained in:
2026-09-30 16:47:18 +02:00
parent e57e2da09d
commit ff9781f56c
25 changed files with 873 additions and 30 deletions

View File

@@ -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">
Dev{label && <span class="dev-banner__crumb">{label}</span>}
</summary>
<div class="dev-banner__body">
{notice && <p class="dev-banner__notice">{notice}</p>}
{here.length > 0 && ( {here.length > 0 && (
<ul> <ul class="dev-banner__list" role="list">
{here.map((d) => ( {here.map((d) => (
<li class={d.level}> <li class:list={['dev-banner__item', `dev-banner__item--${d.level}`]}>
{d.level === 'error' ? '✖' : '▲'} {d.message} {d.message}
</li> </li>
))} ))}
</ul> </ul>
)} )}
{elsewhere.length > 0 && ( {elsewhere.length > 0 && (
<details> <details class="dev-banner__elsewhere">
<summary>{elsewhere.length} problem(s) elsewhere</summary> <summary class="dev-banner__toggle">{elsewhere.length} elsewhere</summary>
<pre>{formatDiagnostics(elsewhere)}</pre> <pre class="dev-banner__log">{formatDiagnostics(elsewhere)}</pre>
</details> </details>
)} )}
</aside> </div>
</details>
)} )}

View 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>
)}

View 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>

View 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>

View 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>

View 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>

View 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: []
---

View File

@@ -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:

View File

@@ -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

View File

@@ -5,3 +5,4 @@ noindex: false
route: {} route: {}
summary: {} summary: {}
--- ---
Set in Blaka{% footnote note="Jdajhdasljkh" /%}.

View File

@@ -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>
{Astro.slots.has('margin') && (
<aside class="margin"> <aside class="margin">
<slot name="margin" /> <slot name="margin" />
</aside> </aside>
)}
{rendered.footnotes.length > 0 && (
<div class="notes content"> <div class="notes content">
<FootnoteList footnotes={rendered.footnotes} /> <FootnoteList footnotes={rendered.footnotes} />
</div> </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>

View File

@@ -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) {

View 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
View 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,
),
}))
}

View File

@@ -61,3 +61,8 @@ export type DirectoryEntry = {
parent: string | null parent: string | null
depth: number depth: number
} }
export type Activity = {
release: Release
touched: boolean
}

View File

@@ -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>

View 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;
}
}

View File

@@ -0,0 +1 @@
@import url("dev-banner.css");

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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");

View 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;
}
}
}

View File

@@ -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");