Barebone Baseline
This commit is contained in:
32
.eslintrc.cjs
Normal file
32
.eslintrc.cjs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
node: true,
|
||||||
|
es2022: true,
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin: @typescript-eslint/recommended',
|
||||||
|
'plugin: astro/recommended',
|
||||||
|
],
|
||||||
|
parser: 'typescript-eslint/parser',
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
sourceType: 'module',
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'no-console': 'warn',
|
||||||
|
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
|
||||||
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['*.astro'],
|
||||||
|
parser: 'astro-eslint-parser',
|
||||||
|
parserOptions: {
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
extraFileExtensions: ['.astro'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
16
.prettierrc
Normal file
16
.prettierrc
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"plugins": ["prettier-plugin-astro"],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": "*.astro",
|
||||||
|
"options": {
|
||||||
|
"parser": "astro"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -2,8 +2,16 @@ import { defineConfig } from 'astro/config';
|
|||||||
import react from '@astrojs/react';
|
import react from '@astrojs/react';
|
||||||
import markdoc from '@astrojs/markdoc';
|
import markdoc from '@astrojs/markdoc';
|
||||||
import keystatic from '@keystatic/astro';
|
import keystatic from '@keystatic/astro';
|
||||||
|
import node from '@astrojs/node';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
output: 'static',
|
||||||
integrations: [react(), markdoc(), keystatic()],
|
integrations: [react(), markdoc(), keystatic()],
|
||||||
|
|
||||||
|
adapter: node({
|
||||||
|
mode: 'standalone',
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,27 +1,13 @@
|
|||||||
import { config, fields, collection } from '@keystatic/core';
|
import { config } from '@keystatic/core';
|
||||||
|
import { articles } from './src/keystatic/collections/articles';
|
||||||
|
import { pages } from './src/keystatic/collections/pages';
|
||||||
|
|
||||||
export default config({
|
export default config({
|
||||||
storage: {
|
storage: {
|
||||||
kind: 'local',
|
kind: 'local',
|
||||||
},
|
},
|
||||||
collections: {
|
collections: {
|
||||||
posts: collection({
|
articles,
|
||||||
label: 'Posts',
|
pages,
|
||||||
slugField: 'title',
|
|
||||||
path: 'src/content/posts/*',
|
|
||||||
format: { contentField: 'content' },
|
|
||||||
schema: {
|
|
||||||
title: fields.slug({ name: { label: 'Title' } }),
|
|
||||||
content: fields.markdoc({
|
|
||||||
label: 'Content',
|
|
||||||
options: {
|
|
||||||
image: {
|
|
||||||
directory: 'src/assets/images/posts',
|
|
||||||
publicPath: '../../assets/images/posts/',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
layout {
|
layout {
|
||||||
|
cwd "~/projects/dave-dmg/"
|
||||||
tab_template name="ui" {
|
tab_template name="ui" {
|
||||||
pane size=1 borderless=true {
|
pane size=1 borderless=true {
|
||||||
plugin location="tab-bar"
|
plugin location="tab-bar"
|
||||||
|
|||||||
5015
package-lock.json
generated
5015
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
24
package.json
24
package.json
@@ -9,14 +9,18 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/markdoc": "^0.12.9",
|
"@astrojs/markdoc": "^0.12.9",
|
||||||
|
"@astrojs/node": "^9.5.4",
|
||||||
"@astrojs/react": "^4.2.0",
|
"@astrojs/react": "^4.2.0",
|
||||||
|
"@fontsource-variable/geist": "^5.2.8",
|
||||||
|
"@fontsource-variable/geist-mono": "^5.2.7",
|
||||||
|
"@fontsource/blaka": "^5.2.7",
|
||||||
|
"@keystatic/astro": "^5.0.6",
|
||||||
|
"@keystatic/core": "^0.5.48",
|
||||||
"@types/react": "^19.0.8",
|
"@types/react": "^19.0.8",
|
||||||
"@types/react-dom": "^19.0.3",
|
"@types/react-dom": "^19.0.3",
|
||||||
"astro": "^5.2.5",
|
"astro": "^5.2.5",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0"
|
||||||
"@keystatic/astro": "^5.0.6",
|
|
||||||
"@keystatic/core": "^0.5.48"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
@@ -24,5 +28,19 @@
|
|||||||
"build": "astro build",
|
"build": "astro build",
|
||||||
"preview": "astro preview",
|
"preview": "astro preview",
|
||||||
"astro": "astro"
|
"astro": "astro"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@csstools/postcss-global-data": "^4.0.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^8.56.0",
|
||||||
|
"@typescript-eslint/parser": "^8.56.0",
|
||||||
|
"eslint": "^10.0.0",
|
||||||
|
"eslint-plugin-astro": "^1.6.0",
|
||||||
|
"postcss-import": "^16.1.1",
|
||||||
|
"postcss-preset-env": "^11.1.3",
|
||||||
|
"prettier": "^3.8.1",
|
||||||
|
"prettier-plugin-astro": "^0.14.1",
|
||||||
|
"stylelint": "^17.3.0",
|
||||||
|
"stylelint-config-standard": "^40.0.0",
|
||||||
|
"stylelint-order": "^7.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
20
postcss.config.mjs
Normal file
20
postcss.config.mjs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import postcssGlobalData from '@csstools/postcss-global-data';
|
||||||
|
import postcssImport from 'postcss-import';
|
||||||
|
import postcssPresetEnv from 'postcss-preset-env';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
plugins: [
|
||||||
|
postcssGlobalData({
|
||||||
|
files: ['./src/styles/custom-media.css'],
|
||||||
|
}),
|
||||||
|
postcssImport(),
|
||||||
|
postcssPresetEnv({
|
||||||
|
stage: 2,
|
||||||
|
features: {
|
||||||
|
'custom-media-queries': true,
|
||||||
|
'nesting-rules': true,
|
||||||
|
'media-query-ranges': true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -1,11 +1,45 @@
|
|||||||
// @ts-ignore
|
|
||||||
import { defineCollection, z } from 'astro:content';
|
import { defineCollection, z } from 'astro:content';
|
||||||
|
|
||||||
const posts = defineCollection({
|
const articles = defineCollection({
|
||||||
// Type-check frontmatter using a schema
|
|
||||||
schema: z.object({
|
schema: z.object({
|
||||||
title: z.string(),
|
title: z.string(),
|
||||||
|
summary: z.string(),
|
||||||
|
cover: z
|
||||||
|
.object({
|
||||||
|
src: z.string().optional(),
|
||||||
|
alt: z.string().optional(),
|
||||||
|
caption: z.string().optional(),
|
||||||
|
showInHeader: z.boolean().default(false),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
publishDate: z.string(),
|
||||||
|
updateDate: z.string().optional(),
|
||||||
|
status: z.enum(['draft', 'published', 'archived']).default('draft'),
|
||||||
|
isFeatured: z.boolean().default(false),
|
||||||
|
parent: z.string().optional(),
|
||||||
|
tags: z.array(z.string()).default([]),
|
||||||
|
relatedArticles: z.array(z.string()).default([]),
|
||||||
|
seo: z
|
||||||
|
.object({
|
||||||
|
title: z.string().optional(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
noIndex: z.boolean().default(false),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const collections = { posts };
|
const pages = defineCollection({
|
||||||
|
schema: z.object({
|
||||||
|
title: z.string(),
|
||||||
|
seo: z
|
||||||
|
.object({
|
||||||
|
title: z.string().optional(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
noIndex: z.boolean().default(false),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const collections = { articles, pages };
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
title: First post
|
|
||||||
---
|
|
||||||
First!
|
|
||||||
|
|
||||||
You can edit this page in the [Admin UI](/keystatic/collection/posts/item/first-post), or directly in your IDE at `src/content/posts/first-post.mdoc`.
|
|
||||||
BIN
src/fonts/UnigrimDee-Regular.woff2
Normal file
BIN
src/fonts/UnigrimDee-Regular.woff2
Normal file
Binary file not shown.
15
src/keystatic/collections/articles.ts
Normal file
15
src/keystatic/collections/articles.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { collection } from '@keystatic/core';
|
||||||
|
import { createBaseArticleFields } from '../fields/base-article.ts';
|
||||||
|
import { createContentField } from '../fields/content.ts';
|
||||||
|
|
||||||
|
export const articles = collection({
|
||||||
|
label: 'Articles',
|
||||||
|
slugField: 'title',
|
||||||
|
path: 'src/content/articles/*/',
|
||||||
|
format: { contentField: 'body' },
|
||||||
|
entryLayout: 'content',
|
||||||
|
schema: {
|
||||||
|
...createBaseArticleFields(),
|
||||||
|
body: createContentField(),
|
||||||
|
},
|
||||||
|
});
|
||||||
16
src/keystatic/collections/pages.ts
Normal file
16
src/keystatic/collections/pages.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { collection, fields } from '@keystatic/core';
|
||||||
|
import { createSEOField } from '../fields/seo.ts';
|
||||||
|
import { createContentField } from '../fields/content.ts';
|
||||||
|
|
||||||
|
export const pages = collection({
|
||||||
|
label: 'Pages',
|
||||||
|
slugField: 'title',
|
||||||
|
path: 'src/content/pages/*',
|
||||||
|
format: { contentField: 'body' },
|
||||||
|
entryLayout: 'content',
|
||||||
|
schema: {
|
||||||
|
title: fields.slug({ name: { label: 'Title' } }),
|
||||||
|
seo: createSEOField(),
|
||||||
|
body: createContentField(),
|
||||||
|
},
|
||||||
|
});
|
||||||
49
src/keystatic/fields/base-article.ts
Normal file
49
src/keystatic/fields/base-article.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { fields } from '@keystatic/core';
|
||||||
|
import { createSEOField } from './seo.ts';
|
||||||
|
import { createCoverField } from './cover.ts';
|
||||||
|
|
||||||
|
export const createBaseArticleFields = () => ({
|
||||||
|
title: fields.slug({ name: { label: 'Title' } }),
|
||||||
|
summary: fields.text({
|
||||||
|
label: 'Summary',
|
||||||
|
multiline: true,
|
||||||
|
validation: { isRequired: true },
|
||||||
|
}),
|
||||||
|
cover: createCoverField(),
|
||||||
|
publishDate: fields.date({
|
||||||
|
label: 'Publish Date',
|
||||||
|
validation: { isRequired: true },
|
||||||
|
}),
|
||||||
|
updateDate: fields.date({
|
||||||
|
label: 'Update Date',
|
||||||
|
}),
|
||||||
|
status: fields.select({
|
||||||
|
label: 'Status',
|
||||||
|
defaultValue: 'draft',
|
||||||
|
options: [
|
||||||
|
{ label: 'Draft', value: 'draft' },
|
||||||
|
{ label: 'Published', value: 'published' },
|
||||||
|
{ label: 'Archived', value: 'archived' },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
isFeatured: fields.checkbox({
|
||||||
|
label: 'Featured',
|
||||||
|
description: 'Show on Homepage',
|
||||||
|
}),
|
||||||
|
parent: fields.relationship({
|
||||||
|
label: 'Parent',
|
||||||
|
collection: 'Articles',
|
||||||
|
}),
|
||||||
|
tags: fields.array(fields.text({ label: 'Tag' }), {
|
||||||
|
label: 'Tags',
|
||||||
|
itemLabel = (props) => props.value,
|
||||||
|
}),
|
||||||
|
relatedArticles: fields.array(
|
||||||
|
fields.relationship({ label: 'Article', collection: 'articles' }),
|
||||||
|
{
|
||||||
|
label: 'Related Articles',
|
||||||
|
itemLabel: (props) => props.value ?? 'Select article',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
seo: createSEOField(),
|
||||||
|
});
|
||||||
15
src/keystatic/fields/content.ts
Normal file
15
src/keystatic/fields/content.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { fields } from '@keystatic/core';
|
||||||
|
import type { ContentComponent } from '@keystatic/core/content-components';
|
||||||
|
|
||||||
|
export const sharedComponents: Record<string, ContentComponent> = {};
|
||||||
|
|
||||||
|
export const createContentField = (
|
||||||
|
additionalComponents?: Record<string, ContentComponent>,
|
||||||
|
) =>
|
||||||
|
fields.markdoc({
|
||||||
|
label: 'Body',
|
||||||
|
components: {
|
||||||
|
...sharedComponents,
|
||||||
|
...additionalComponents,
|
||||||
|
},
|
||||||
|
});
|
||||||
21
src/keystatic/fields/cover.ts
Normal file
21
src/keystatic/fields/cover.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { fields } from '@keystatic/core';
|
||||||
|
|
||||||
|
export const createCoverField = () =>
|
||||||
|
fields.object(
|
||||||
|
{
|
||||||
|
src: fields.object({
|
||||||
|
label: 'Cover Image',
|
||||||
|
directory: 'src/content/articles',
|
||||||
|
publicPath: '/content/articles',
|
||||||
|
}),
|
||||||
|
alt: fields.text({ label: 'Alt Text' }),
|
||||||
|
caption: fields.text({ label: 'Caption', multline: true }),
|
||||||
|
showInHeader: fields.checkbox({
|
||||||
|
label: 'Show in header',
|
||||||
|
defaultValue: false,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Cover Image',
|
||||||
|
},
|
||||||
|
);
|
||||||
0
src/keystatic/fields/pages.ts
Normal file
0
src/keystatic/fields/pages.ts
Normal file
23
src/keystatic/fields/seo.ts
Normal file
23
src/keystatic/fields/seo.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { fields } from '@keystatic/core';
|
||||||
|
|
||||||
|
export const createSEOField = () =>
|
||||||
|
fields.object(
|
||||||
|
{
|
||||||
|
title: fields.text({
|
||||||
|
label: 'SEO Title',
|
||||||
|
validation: { length: { max: 60 } },
|
||||||
|
}),
|
||||||
|
description: fields.text({
|
||||||
|
label: 'SEO Description',
|
||||||
|
multiline: true,
|
||||||
|
validation: { length: { max: 160 } },
|
||||||
|
}),
|
||||||
|
noIndex: fields.checkbox({
|
||||||
|
label: 'No Index',
|
||||||
|
description: 'Prevents search engines from indexing',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'SEO Settings',
|
||||||
|
},
|
||||||
|
);
|
||||||
22
src/layouts/ArticleLayout.astro
Normal file
22
src/layouts/ArticleLayout.astro
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
import type { CollectionEntry } from 'astro:content';
|
||||||
|
import Base from './Base.astro';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
entry: CollectionEntry<'articles'>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { entry } = Astro.props;
|
||||||
|
const { Content } = await entry.render();
|
||||||
|
const { title, summary, publishDate, updateDate, cover, tags, seo } =
|
||||||
|
entry.data;
|
||||||
|
---
|
||||||
|
|
||||||
|
<Base title={title} seo={seo}>
|
||||||
|
<main class="content">
|
||||||
|
<article>
|
||||||
|
<h1>{title}</h1>
|
||||||
|
<Content />
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
</Base>
|
||||||
30
src/layouts/Base.astro
Normal file
30
src/layouts/Base.astro
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
import '../styles.css';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
title: string;
|
||||||
|
seo?: {
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
noIndex?: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const { title, seo } = Astro.props;
|
||||||
|
const pageTitle = seo?.title || title;
|
||||||
|
const pageDescription = seo?.description || '';
|
||||||
|
---
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>{title}</title>
|
||||||
|
{pageDescription && <meta name="description" content={pageDescription} />}
|
||||||
|
{seo?.noIndex && <meta name="robots" content="noindex" />}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<slot />
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
import '../styles.css'
|
|
||||||
|
|
||||||
export interface Props {
|
|
||||||
title: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { title } = Astro.props;
|
|
||||||
---
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="description" content="Astro description">
|
|
||||||
<meta name="viewport" content="width=device-width" />
|
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
||||||
<meta name="generator" content={Astro.generator} />
|
|
||||||
<title>{title}</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<slot />
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
18
src/layouts/PageLayout.astro
Normal file
18
src/layouts/PageLayout.astro
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
import type { CollectionEntry } from 'astro:content';
|
||||||
|
import Base from './Base.astro';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
entry: CollectionEntry<'pages'>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { entry } = Astro.props;
|
||||||
|
const { Content } = await entry.render();
|
||||||
|
const { title, seo } = entry.data;
|
||||||
|
---
|
||||||
|
|
||||||
|
<Base title={title} seo={seo}>
|
||||||
|
<main class="content">
|
||||||
|
<Content />
|
||||||
|
</main>
|
||||||
|
</Base>
|
||||||
56
src/lib/paths.ts
Normal file
56
src/lib/paths.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { getCollection, type CollectionEntry } from 'astro:content';
|
||||||
|
|
||||||
|
export type ContentType = 'article' | 'page';
|
||||||
|
|
||||||
|
export interface ResolvedEntry {
|
||||||
|
type: ContentType;
|
||||||
|
path: string;
|
||||||
|
entry: CollectionEntry<'pages'> | CollectionEntry<'articles'>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPath(
|
||||||
|
slug: string,
|
||||||
|
parentSlug: string | null,
|
||||||
|
articles: CollectionEntry<'articles'>[],
|
||||||
|
visited: Set<string> = new Set(),
|
||||||
|
): string {
|
||||||
|
if (!parentSlug) return slug;
|
||||||
|
if (visited.has(parentSlug)) {
|
||||||
|
console.warn(`Circular reference detected at ${parentSlug}`);
|
||||||
|
return slug;
|
||||||
|
}
|
||||||
|
visited.add(parentSlug);
|
||||||
|
|
||||||
|
const parent = articles.find((a) => a.slug === parentSlug);
|
||||||
|
if (!parent) return slug;
|
||||||
|
|
||||||
|
const parentPath = buildPath(
|
||||||
|
parent.slug,
|
||||||
|
parent.data.parent ?? null,
|
||||||
|
articles,
|
||||||
|
visited,
|
||||||
|
);
|
||||||
|
|
||||||
|
return `${parentPath}/${slug}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resolveAllPaths(): Promise<ResolvedEntry[]> {
|
||||||
|
const articles = await getCollection('articles');
|
||||||
|
const pages = await getCollection('pages');
|
||||||
|
|
||||||
|
const resolvedArticles: ResolvedEntry[] = articles
|
||||||
|
.filter((article) => article.data.status === 'published')
|
||||||
|
.map((article) => ({
|
||||||
|
type: 'article',
|
||||||
|
path: buildPath(article.slug, article.data.parent ?? null, articles),
|
||||||
|
entry: article,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const resolvedPages: ResolvedEntry[] = pages.map((page) => ({
|
||||||
|
type: 'page',
|
||||||
|
path: `static/${page.slug}`,
|
||||||
|
entry: page,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return [...resolvedArticles, ...resolvedPages];
|
||||||
|
}
|
||||||
@@ -1,29 +1,10 @@
|
|||||||
---
|
---
|
||||||
import { getCollection } from 'astro:content';
|
import Base from '../layouts/Base.astro';
|
||||||
import Layout from '../layouts/Layout.astro';
|
|
||||||
|
|
||||||
const posts = await getCollection('posts');
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout title="Keystatic & Astro's Content Collections">
|
<Base title="dave|dmg">
|
||||||
<div>
|
<main class="content">
|
||||||
<h1>Keystatic ⚡️ + Astro 🚀</h1>
|
<h1>dave|dmg</h1>
|
||||||
<p>
|
<p>Under Construction</p>
|
||||||
This homepage shows how to load data from Astro's content collections.
|
</main>
|
||||||
</p>
|
</Base>
|
||||||
<p>
|
|
||||||
<a href="/keystatic">Click here to visit the Admin UI</a>, or the link
|
|
||||||
below to view a post in the collection.
|
|
||||||
</p>
|
|
||||||
<h2>Posts</h2>
|
|
||||||
<ul>
|
|
||||||
{
|
|
||||||
posts.map(post => (
|
|
||||||
<li>
|
|
||||||
<a href={`/posts/${post.slug}`}>{post.data.title}</a>
|
|
||||||
</li>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</Layout>
|
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
---
|
|
||||||
import { getCollection, getEntry } from 'astro:content';
|
|
||||||
import Layout from '../../layouts/Layout.astro';
|
|
||||||
|
|
||||||
const { slug } = Astro.params;
|
|
||||||
if (!slug) throw new Error('Slug not found');
|
|
||||||
const post = await getEntry('posts', slug);
|
|
||||||
|
|
||||||
if (!post) throw new Error('No post found for this slug');
|
|
||||||
const { Content } = await post.render();
|
|
||||||
|
|
||||||
// Generate static pages
|
|
||||||
export async function getStaticPaths() {
|
|
||||||
const posts = await getCollection('posts');
|
|
||||||
return posts.map(post => ({ params: { slug: post.slug } }));
|
|
||||||
}
|
|
||||||
---
|
|
||||||
|
|
||||||
<Layout title={post.data.title}>
|
|
||||||
<h1>{post.data.title}</h1>
|
|
||||||
<Content />
|
|
||||||
</Layout>
|
|
||||||
@@ -1,29 +1,8 @@
|
|||||||
html {
|
@import 'styles/fonts.css';
|
||||||
max-width: 70ch;
|
@import 'styles/foundation.css';
|
||||||
padding: 3rem 1rem;
|
@import 'styles/colors.css';
|
||||||
margin: auto;
|
@import 'styles/dimensions.css';
|
||||||
line-height: 1.75;
|
@import 'styles/typography.css';
|
||||||
font-size: 1.25rem;
|
@import 'styles/elements.css';
|
||||||
font-family: sans-serif;
|
@import 'styles/custom-media.css';
|
||||||
}
|
@import 'styles/content.css';
|
||||||
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5,
|
|
||||||
h6 {
|
|
||||||
margin: 1rem 0 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
p,
|
|
||||||
ul,
|
|
||||||
ol {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
color: #1d1d1d;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
height: auto;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|||||||
112
src/styles/colors.css
Normal file
112
src/styles/colors.css
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
:root {
|
||||||
|
/* === Palette === */
|
||||||
|
|
||||||
|
/* == Neutrals == */
|
||||||
|
--color-palette-black-ink: oklch(21.9% 0.006 285.911deg);
|
||||||
|
--color-palette-charcoal: oklch(26.6% 0.008 240.166deg);
|
||||||
|
--color-palette-charcoal-gray: oklch(33.7% 0.026 266.801deg);
|
||||||
|
--color-palette-carbon: oklch(39.8% 0 89.876deg);
|
||||||
|
--color-palette-cloud-gray: oklch(53.6% 0.005 236.565deg);
|
||||||
|
--color-palette-stone: oklch(64.3% 0.005 91.471deg);
|
||||||
|
--color-palette-silver: oklch(72.9% 0.001 17.185deg);
|
||||||
|
--color-palette-light-silver: oklch(82.6% 0.002 247.844deg);
|
||||||
|
--color-palette-pale-gray: oklch(91.3% 0.004 91.449deg);
|
||||||
|
--color-palette-very-pale-gray: oklch(94.3% 0 89.876deg);
|
||||||
|
--color-palette-off-white: oklch(98.1% 0.003 247.858deg);
|
||||||
|
--color-palette-ice-blue: oklch(99.9% 0.001 197.139deg);
|
||||||
|
|
||||||
|
/* == Accents == */
|
||||||
|
--color-palette-lava: oklch(63.6% 0.193 17.075deg);
|
||||||
|
--color-palette-cerise: oklch(60.7% 0.23 18.554deg);
|
||||||
|
--color-palette-persimmon: oklch(68.6% 0.179 40.01deg);
|
||||||
|
--color-palette-fuchsia: oklch(49.6% 0.181 351.176deg);
|
||||||
|
--color-palette-bright-canary: oklch(89% 0.157 97.726deg);
|
||||||
|
--color-palette-lime-green: oklch(74.6% 0.18 129.939deg);
|
||||||
|
--color-palette-royal-blue: oklch(46.1% 0.07 245.64deg);
|
||||||
|
--color-palette-electric-blue: oklch(90.8% 0.128 188.419deg);
|
||||||
|
|
||||||
|
/* == System Accents (reserve) == */
|
||||||
|
--color-palette-warhammer: oklch(
|
||||||
|
43.2% 0.169 7.14deg
|
||||||
|
); /* Crimson — Empire blood red */
|
||||||
|
--color-palette-mordheim: oklch(
|
||||||
|
82.7% 0.047 76.752deg
|
||||||
|
); /* Tan — ruined city stone */
|
||||||
|
--color-palette-unknown-armies: oklch(
|
||||||
|
49.6% 0.181 351.176deg
|
||||||
|
); /* Fuchsia — occult magick */
|
||||||
|
--color-palette-sla-industries: oklch(
|
||||||
|
90.8% 0.128 188.419deg
|
||||||
|
); /* Electric blue — corporate neon */
|
||||||
|
|
||||||
|
/* === Semantic Colors === */
|
||||||
|
|
||||||
|
/* == Primary / Secondary / Tertiary == */
|
||||||
|
--color-primary: var(--color-palette-lava);
|
||||||
|
--color-primary-surface: oklch(from var(--color-primary) calc(l + 0.1) c h);
|
||||||
|
--color-primary-emphasis: oklch(from var(--color-primary) calc(l - 0.15) c h);
|
||||||
|
|
||||||
|
--color-secondary: var(--color-palette-lime-green);
|
||||||
|
--color-secondary-surface: oklch(
|
||||||
|
from var(--color-secondary) calc(l + 0.1) c h
|
||||||
|
);
|
||||||
|
--color-secondary-emphasis: oklch(
|
||||||
|
from var(--color-secondary) calc(l - 0.15) c h
|
||||||
|
);
|
||||||
|
|
||||||
|
--color-tertiary: var(--color-palette-bright-canary);
|
||||||
|
--color-tertiary-surface: oklch(from var(--color-tertiary) calc(l + 0.1) c h);
|
||||||
|
--color-tertiary-emphasis: oklch(
|
||||||
|
from var(--color-tertiary) calc(l - 0.15) c h
|
||||||
|
);
|
||||||
|
|
||||||
|
/* == Text == */
|
||||||
|
--color-text-primary: var(--color-palette-black-ink);
|
||||||
|
--color-text-secondary: var(--color-palette-charcoal);
|
||||||
|
--color-text-tertiary: var(--color-palette-charcoal-gray);
|
||||||
|
--color-text-quarternary: var(--color-palette-carbon);
|
||||||
|
--color-text-inverse: var(--color-palette-ice-blue);
|
||||||
|
--color-text-disabled: var(--color-palette-cloud-gray);
|
||||||
|
|
||||||
|
/* == Surfaces == */
|
||||||
|
--color-surface-base: var(--color-palette-ice-blue);
|
||||||
|
--color-surface-elevated-1: var(--color-palette-off-white);
|
||||||
|
--color-surface-elevated-2: var(--color-palette-very-pale-gray);
|
||||||
|
--color-surface-elevated-3: var(--color-palette-pale-gray);
|
||||||
|
--color-surface-elevated-4: var(--color-palette-light-silver);
|
||||||
|
--color-surface-inverse: var(--color-palette-black-ink);
|
||||||
|
|
||||||
|
/* == Borders == */
|
||||||
|
--color-border-subtle: var(--color-palette-silver);
|
||||||
|
--color-border-normal: var(--color-palette-stone);
|
||||||
|
--color-border-strong: var(--color-palette-charcoal-gray);
|
||||||
|
|
||||||
|
/* == States == */
|
||||||
|
--color-state-error: var(--color-palette-cerise);
|
||||||
|
--color-state-warning: var(--color-palette-persimmon);
|
||||||
|
--color-state-success: var(--color-palette-lime-green);
|
||||||
|
--color-state-info: var(--color-palette-royal-blue);
|
||||||
|
|
||||||
|
/* == Links == */
|
||||||
|
--color-text-link: var(--color-text-tertiary);
|
||||||
|
--color-text-link-hover: var(--color-secondary);
|
||||||
|
--color-text-link-visited: var(--color-palette-fuchsia);
|
||||||
|
|
||||||
|
/* == Focus == */
|
||||||
|
--color-focus-ring: var(--color-primary);
|
||||||
|
|
||||||
|
/* == Overlays == */
|
||||||
|
--color-overlay-light: oklch(0% 0 0deg / 10%);
|
||||||
|
--color-overlay-medium: oklch(0% 0 0deg / 30%);
|
||||||
|
--color-overlay-heavy: oklch(0% 0 0deg / 60%);
|
||||||
|
|
||||||
|
/* == Shadows == */
|
||||||
|
--color-shadow: oklch(0% 0 0deg / 10%);
|
||||||
|
--color-shadow-strong: oklch(0% 0 0deg / 25%);
|
||||||
|
|
||||||
|
/* == Utility == */
|
||||||
|
--color-highlight: var(--color-tertiary-surface);
|
||||||
|
--color-highlight-strong: var(--color-tertiary);
|
||||||
|
--color-skeleton-base: var(--color-surface-elevated-2);
|
||||||
|
--color-skeleton-shimmer: var(--color-surface-elevated-1);
|
||||||
|
}
|
||||||
660
src/styles/content.css
Normal file
660
src/styles/content.css
Normal file
@@ -0,0 +1,660 @@
|
|||||||
|
.content {
|
||||||
|
max-width: clamp(60ch, 90vw, 90ch);
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 var(--spacing-comfortable);
|
||||||
|
font-size: var(--typo-size-responsive);
|
||||||
|
|
||||||
|
/* === Headings === */
|
||||||
|
|
||||||
|
& h1 {
|
||||||
|
margin-block: var(--el-h1-vspace-top) var(--el-h1-vspace-bottom);
|
||||||
|
padding-bottom: var(--spacing-snug);
|
||||||
|
border-bottom: var(--size-4) solid var(--el-h1-color);
|
||||||
|
|
||||||
|
font-family: var(--el-h1-font-family), serif;
|
||||||
|
font-size: var(--el-h1-font-size);
|
||||||
|
font-weight: 900;
|
||||||
|
line-height: 1.125;
|
||||||
|
color: var(--el-h1-color);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: -0.0137em;
|
||||||
|
|
||||||
|
& + p,
|
||||||
|
& + ul,
|
||||||
|
& + ol,
|
||||||
|
& + blockquote {
|
||||||
|
margin-block: calc(1em * var(--typo-leading-normal) * var(--vspace-tight))
|
||||||
|
calc(1em * var(--typo-leading-normal) * var(--vspace-snug));
|
||||||
|
}
|
||||||
|
|
||||||
|
& + h2 {
|
||||||
|
margin-block: calc(1em * 1.125 * var(--vspace-snug))
|
||||||
|
calc(1em * 1.125 * var(--vspace-normal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& h2 {
|
||||||
|
margin-block: var(--el-h2-vspace-top) var(--el-h2-vspace-bottom);
|
||||||
|
padding-left: var(--spacing-snug);
|
||||||
|
border-left: var(--size-4) solid var(--el-h2-color);
|
||||||
|
|
||||||
|
font-family: var(--el-h2-font-family), serif;
|
||||||
|
font-size: var(--el-h2-font-size);
|
||||||
|
font-weight: 900;
|
||||||
|
line-height: 1.1765;
|
||||||
|
color: var(--el-h2-color);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: -0.0096em;
|
||||||
|
|
||||||
|
& + p,
|
||||||
|
& + ul,
|
||||||
|
& + ol,
|
||||||
|
& + blockquote {
|
||||||
|
margin-block: calc(1em * var(--typo-leading-normal) * var(--vspace-tight))
|
||||||
|
calc(1em * var(--typo-leading-normal) * var(--vspace-snug));
|
||||||
|
}
|
||||||
|
|
||||||
|
& + h3 {
|
||||||
|
margin-block: calc(1em * 1.125 * var(--vspace-compressed))
|
||||||
|
calc(1em * 1.125 * var(--vspace-snug));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& h3 {
|
||||||
|
margin-block: var(--el-h3-vspace-top) var(--el-h3-vspace-bottom);
|
||||||
|
padding: var(--spacing-tight) var(--spacing-snug);
|
||||||
|
|
||||||
|
font-family: var(--el-h3-font-family), serif;
|
||||||
|
font-size: var(--el-h3-font-size);
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 1.2;
|
||||||
|
color: var(--color-surface-base);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: -0.004em;
|
||||||
|
|
||||||
|
background: var(--el-h3-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
& h4 {
|
||||||
|
margin-block: var(--el-h4-vspace-top) var(--el-h4-vspace-bottom);
|
||||||
|
padding: var(--spacing-tight) var(--spacing-snug);
|
||||||
|
border-top: var(--size-3) solid var(--el-h4-color);
|
||||||
|
border-bottom: var(--size-3) solid var(--el-h4-color);
|
||||||
|
|
||||||
|
font-family: var(--el-h4-font-family), serif;
|
||||||
|
font-size: var(--el-h4-font-size);
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 1.125;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.0025em;
|
||||||
|
}
|
||||||
|
|
||||||
|
& h5 {
|
||||||
|
margin-block: var(--el-h5-vspace-top) var(--el-h5-vspace-bottom);
|
||||||
|
padding: var(--spacing-tight) var(--spacing-snug);
|
||||||
|
|
||||||
|
font-family: var(--el-h5-font-family), serif;
|
||||||
|
font-size: var(--el-h5-font-size);
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 1.28;
|
||||||
|
color: var(--el-h5-color);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
padding-right: var(--spacing-tight);
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '⭑';
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& h6 {
|
||||||
|
margin-block: var(--el-h6-vspace-top) var(--el-h6-vspace-bottom);
|
||||||
|
|
||||||
|
font-family: var(--el-h6-font-family), serif;
|
||||||
|
font-size: var(--el-h6-font-size);
|
||||||
|
font-weight: 900;
|
||||||
|
line-height: 1.4;
|
||||||
|
color: var(--el-h6-color);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.035em;
|
||||||
|
}
|
||||||
|
|
||||||
|
& h3,
|
||||||
|
& h4,
|
||||||
|
& h5,
|
||||||
|
& h6 {
|
||||||
|
& + p,
|
||||||
|
& + ul,
|
||||||
|
& + ol,
|
||||||
|
& + blockquote {
|
||||||
|
margin-block: calc(
|
||||||
|
1em * var(--typo-leading-normal) * var(--vspace-compressed)
|
||||||
|
)
|
||||||
|
calc(1em * var(--typo-leading-normal) * var(--vspace-snug));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Body Text === */
|
||||||
|
|
||||||
|
& p {
|
||||||
|
margin-block: var(--el-p-vspace-top) var(--el-p-vspace-bottom);
|
||||||
|
|
||||||
|
font-family: var(--el-p-font-family), sans-serif;
|
||||||
|
font-size: var(--el-p-font-size);
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: var(--typo-leading-normal);
|
||||||
|
color: var(--el-p-color);
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
|
||||||
|
& blockquote {
|
||||||
|
margin-block: var(--el-p-vspace-top) var(--el-p-vspace-bottom);
|
||||||
|
padding: var(--spacing-snug) 0 var(--spacing-snug)
|
||||||
|
var(--spacing-comfortable);
|
||||||
|
border-left: var(--size-4) solid var(--color-text-tertiary);
|
||||||
|
|
||||||
|
font-family: var(--el-blockquote-font-family), serif;
|
||||||
|
font-size: var(--el-blockquote-font-size);
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
line-height: var(--typo-leading-comfortable);
|
||||||
|
color: var(--el-blockquote-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Code === */
|
||||||
|
|
||||||
|
pre {
|
||||||
|
margin-block: var(--el-pre-vspace-top) var(--el-pre-vspace-bottom);
|
||||||
|
padding: var(--spacing-comfortable);
|
||||||
|
|
||||||
|
font-family: var(--el-pre-font-family), monospace;
|
||||||
|
font-size: var(--el-pre-font-size);
|
||||||
|
line-height: 1.5385;
|
||||||
|
color: var(--el-pre-color);
|
||||||
|
|
||||||
|
background: var(--el-pre-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
padding: 0.1em 0.3em;
|
||||||
|
|
||||||
|
font-family: var(--font-mono), monospace;
|
||||||
|
font-size: var(--typo-size-sm);
|
||||||
|
color: var(--color-text-inverse);
|
||||||
|
|
||||||
|
background: var(--color-surface-inverse);
|
||||||
|
}
|
||||||
|
|
||||||
|
kbd {
|
||||||
|
padding: 0.1em 0.3em;
|
||||||
|
border: 1px solid var(--color-text-primary);
|
||||||
|
border-radius: 2px;
|
||||||
|
|
||||||
|
font-family: var(--font-mono), monospace;
|
||||||
|
font-size: var(--typo-size-xs);
|
||||||
|
color: var(--color-text-inverse);
|
||||||
|
text-transform: uppercase;
|
||||||
|
|
||||||
|
background: var(--color-surface-inverse);
|
||||||
|
}
|
||||||
|
|
||||||
|
samp {
|
||||||
|
padding: 0.1em 0.3em;
|
||||||
|
border-left: var(--size-1) solid var(--color-text-tertiary);
|
||||||
|
|
||||||
|
font-family: var(--font-mono), monospace;
|
||||||
|
font-size: var(--typo-size-sm);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
|
||||||
|
background: var(--color-surface-elevated-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
var {
|
||||||
|
font-family: var(--font-mono), monospace;
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: normal;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Lists === */
|
||||||
|
|
||||||
|
ul,
|
||||||
|
ol {
|
||||||
|
margin-block: var(--el-list-vspace-top) var(--el-list-vspace-bottom);
|
||||||
|
font-size: var(--el-list-font-size);
|
||||||
|
line-height: var(--typo-leading-normal);
|
||||||
|
color: var(--el-list-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul ul,
|
||||||
|
ul ol,
|
||||||
|
ol ul,
|
||||||
|
ol ol {
|
||||||
|
margin-block: var(--el-list-nested-vspace-top)
|
||||||
|
var(--el-list-nested-vspace-bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-block: var(--el-li-vspace-top) var(--el-li-vspace-bottom);
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-block: var(--el-li-nested-vspace-top)
|
||||||
|
var(--el-li-nested-vspace-bottom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding-left: var(--spacing-cozy);
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
li {
|
||||||
|
padding-left: var(--spacing-cozy);
|
||||||
|
|
||||||
|
&::marker {
|
||||||
|
content: '⯃';
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul,
|
||||||
|
ol {
|
||||||
|
padding-left: var(--spacing-cozy);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul li::marker {
|
||||||
|
content: '⯁';
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul ul,
|
||||||
|
ul ol,
|
||||||
|
ol ul,
|
||||||
|
ol ol {
|
||||||
|
padding-left: var(--spacing-cozy);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul ul li::marker,
|
||||||
|
ol ul li::marker {
|
||||||
|
content: '⯆';
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul ul ul,
|
||||||
|
ul ul ol,
|
||||||
|
ul ol ul,
|
||||||
|
ul ol ol,
|
||||||
|
ol ul ul,
|
||||||
|
ol ul ol,
|
||||||
|
ol ol ul,
|
||||||
|
ol ol ol {
|
||||||
|
padding-left: var(--spacing-cozy);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul ul ul li::marker,
|
||||||
|
ul ol ul li::marker,
|
||||||
|
ol ul ul li::marker,
|
||||||
|
ol ol ul li::marker {
|
||||||
|
content: '⯀';
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ol {
|
||||||
|
counter-reset: ol-l1;
|
||||||
|
contain: style;
|
||||||
|
padding-left: var(--spacing-cozy);
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
& > li {
|
||||||
|
counter-increment: ol-l1;
|
||||||
|
margin-left: 1.5em;
|
||||||
|
padding-left: var(--spacing-cozy);
|
||||||
|
|
||||||
|
&::marker {
|
||||||
|
content: counter(ol-l1, decimal-leading-zero) '.)';
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ol > li > ol {
|
||||||
|
counter-reset: ol-l2;
|
||||||
|
padding-left: var(--spacing-cozy);
|
||||||
|
|
||||||
|
& > li {
|
||||||
|
counter-increment: ol-l2;
|
||||||
|
margin-left: 0.825em;
|
||||||
|
|
||||||
|
&::marker {
|
||||||
|
content: counter(ol-l2, lower-alpha) '.)';
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ol > li > ul {
|
||||||
|
padding-left: var(--spacing-cozy);
|
||||||
|
}
|
||||||
|
|
||||||
|
ol > li > ol > li > ol,
|
||||||
|
ol > li > ul > li > ol {
|
||||||
|
counter-reset: ol-l3;
|
||||||
|
margin-left: 0;
|
||||||
|
padding-left: var(--spacing-cozy);
|
||||||
|
|
||||||
|
& > li {
|
||||||
|
counter-increment: ol-l3;
|
||||||
|
|
||||||
|
&::marker {
|
||||||
|
content: counter(ol-l3, upper-roman) '.)';
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ol > li > ol > li > ul,
|
||||||
|
ol > li > ul > li > ul {
|
||||||
|
padding-left: var(--spacing-cozy);
|
||||||
|
}
|
||||||
|
|
||||||
|
ol > li > ol > li > ol > li > ol,
|
||||||
|
ol > li > ol > li > ul > li > ol,
|
||||||
|
ol > li > ul > li > ol > li > ol,
|
||||||
|
ol > li > ul > li > ul > li > ol {
|
||||||
|
counter-reset: ol-l4;
|
||||||
|
margin-left: 0;
|
||||||
|
padding-left: var(--spacing-cozy);
|
||||||
|
|
||||||
|
& > li {
|
||||||
|
counter-increment: ol-l4;
|
||||||
|
|
||||||
|
&::marker {
|
||||||
|
content: counter(ol-l4, lower-greek) '.)';
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ol > li > ol > li > ol > li > ul,
|
||||||
|
ol > li > ol > li > ul > li > ul,
|
||||||
|
ol > li > ul > li > ol > li > ul,
|
||||||
|
ol > li > ul > li > ul > li > ul {
|
||||||
|
padding-left: var(--spacing-cozy);
|
||||||
|
}
|
||||||
|
|
||||||
|
ol li,
|
||||||
|
ul li {
|
||||||
|
list-style-position: outside;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-inside ol li,
|
||||||
|
.list-inside ul li {
|
||||||
|
list-style-position: inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Tables === */
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
margin-block: var(--spacing-tight) var(--spacing-tight);
|
||||||
|
border: var(--size-2) solid var(--color-text-primary);
|
||||||
|
|
||||||
|
font-size: var(--typo-size-sm);
|
||||||
|
line-height: 1.2;
|
||||||
|
|
||||||
|
& thead th,
|
||||||
|
& th {
|
||||||
|
padding: 0 var(--spacing-snug) var(--spacing-snug);
|
||||||
|
|
||||||
|
font-family: var(--el-th-font-family), serif;
|
||||||
|
font-size: var(--typo-size-sm);
|
||||||
|
font-weight: 900;
|
||||||
|
line-height: 1.2;
|
||||||
|
color: var(--el-th-color);
|
||||||
|
text-transform: uppercase;
|
||||||
|
|
||||||
|
background: var(--el-th-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
& tbody td,
|
||||||
|
& td {
|
||||||
|
padding: var(--spacing-snug);
|
||||||
|
border: var(--size-1) solid var(--color-text-secondary);
|
||||||
|
|
||||||
|
font-family: var(--el-td-font-family), monospace;
|
||||||
|
font-size: var(--typo-size-sm);
|
||||||
|
line-height: 1.2;
|
||||||
|
color: var(--el-td-color);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === DL / DT / DD === */
|
||||||
|
|
||||||
|
dl {
|
||||||
|
margin-block: calc(1em * var(--typo-leading-normal) * var(--vspace-snug))
|
||||||
|
calc(1em * var(--typo-leading-normal) * var(--vspace-compressed));
|
||||||
|
font-size: var(--typo-size-md);
|
||||||
|
line-height: var(--typo-leading-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
dt {
|
||||||
|
margin-block: calc(1em * 1.4 * var(--vspace-snug))
|
||||||
|
calc(1em * 1.4 * var(--vspace-compressed));
|
||||||
|
padding: var(--spacing-snug);
|
||||||
|
|
||||||
|
font-family: var(--el-dt-font-family), serif;
|
||||||
|
font-size: var(--typo-size-lg);
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.4;
|
||||||
|
color: var(--el-dt-color);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.035em;
|
||||||
|
|
||||||
|
background: var(--el-dt-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
margin-block: calc(
|
||||||
|
1em * var(--typo-leading-normal) * var(--vspace-compressed)
|
||||||
|
)
|
||||||
|
calc(1em * var(--typo-leading-normal) * var(--vspace-tight));
|
||||||
|
padding: 0 var(--spacing-comfortable);
|
||||||
|
|
||||||
|
font-family: var(--el-dd-font-family), sans-serif;
|
||||||
|
font-size: var(--typo-size-md);
|
||||||
|
line-height: var(--typo-leading-normal);
|
||||||
|
color: var(--el-dd-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === HR === */
|
||||||
|
|
||||||
|
hr {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
overflow: visible;
|
||||||
|
|
||||||
|
height: var(--size-3);
|
||||||
|
margin-block: var(--spacing-relaxed) 0;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
background: var(--hr-color);
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '▼';
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
|
||||||
|
padding: 0 0.25em;
|
||||||
|
|
||||||
|
font-size: var(--typo-size-4xl);
|
||||||
|
line-height: var(--typo-leading-compressed);
|
||||||
|
color: var(--hr-symbol-color);
|
||||||
|
|
||||||
|
background: var(--hr-symbol-background);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Inline Elements === */
|
||||||
|
|
||||||
|
em,
|
||||||
|
i {
|
||||||
|
padding: 0.1em 0.2em;
|
||||||
|
|
||||||
|
font-style: normal;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.025em;
|
||||||
|
|
||||||
|
background: var(--color-surface-elevated-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
strong,
|
||||||
|
b {
|
||||||
|
font-weight: 900;
|
||||||
|
letter-spacing: 0.025em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
padding: 0.1em 0.2em;
|
||||||
|
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-thickness: var(--size-1);
|
||||||
|
text-underline-offset: 2px;
|
||||||
|
|
||||||
|
transition:
|
||||||
|
color 0.5s ease-in-out,
|
||||||
|
background 0.5s ease-in-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--color-surface-base);
|
||||||
|
text-decoration: none;
|
||||||
|
background: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:visited,
|
||||||
|
&:active {
|
||||||
|
color: var(--color-primary-emphasis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: var(--typo-size-xs);
|
||||||
|
color: var(--color-text-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 0.5625em;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
del,
|
||||||
|
s {
|
||||||
|
color: var(--color-text-tertiary);
|
||||||
|
text-decoration: line-through;
|
||||||
|
text-decoration-thickness: var(--size-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ins {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-color: var(--color-primary);
|
||||||
|
text-decoration-thickness: var(--size-1);
|
||||||
|
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
abbr {
|
||||||
|
cursor: help;
|
||||||
|
text-decoration: underline dotted;
|
||||||
|
text-underline-offset: var(--size-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
dfn {
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
cite {
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: normal;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
q {
|
||||||
|
font-style: normal;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '»';
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '«';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time {
|
||||||
|
font-family: var(--font-mono), monospace;
|
||||||
|
font-size: var(--typo-size-sm);
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === Article Lead Paragraph === */
|
||||||
|
|
||||||
|
& article {
|
||||||
|
& > p:first-of-type {
|
||||||
|
font-size: var(--typo-size-xl);
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.4;
|
||||||
|
letter-spacing: 0.022em;
|
||||||
|
|
||||||
|
&::first-letter {
|
||||||
|
float: left;
|
||||||
|
|
||||||
|
margin: 0.1em 0.1em 0 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
font-family: var(--font-display), serif;
|
||||||
|
font-size: 4em;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1;
|
||||||
|
color: var(--color-primary);
|
||||||
|
|
||||||
|
@supports (initial-letter: 4) {
|
||||||
|
float: none;
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
padding-right: var(--spacing-snug);
|
||||||
|
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
|
||||||
|
initial-letter: 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/styles/custom-media.css
Normal file
5
src/styles/custom-media.css
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
@custom-media --bp-mobile (width < 48rem);
|
||||||
|
@custom-media --bp-tablet (width >= 48rem) and (width < 80rem);
|
||||||
|
@custom-media --bp-tablet-up (width >= 48rem);
|
||||||
|
@custom-media --bp-tablet-down (width < 80rem);
|
||||||
|
@custom-media --bp-desktop (width >= 80rem);
|
||||||
42
src/styles/dimensions.css
Normal file
42
src/styles/dimensions.css
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
:root {
|
||||||
|
/* === Size Scale === */
|
||||||
|
--size-0: 0;
|
||||||
|
--size-px: 1px;
|
||||||
|
--size-05: 0.125rem;
|
||||||
|
--size-1: 0.25rem;
|
||||||
|
--size-2: 0.5rem;
|
||||||
|
--size-3: 0.75rem;
|
||||||
|
--size-4: 1rem;
|
||||||
|
--size-5: 1.25rem;
|
||||||
|
--size-6: 1.5rem;
|
||||||
|
--size-8: 2rem;
|
||||||
|
--size-10: 2.5rem;
|
||||||
|
--size-12: 3rem;
|
||||||
|
--size-16: 4rem;
|
||||||
|
--size-20: 5rem;
|
||||||
|
--size-24: 6rem;
|
||||||
|
--size-32: 8rem;
|
||||||
|
--size-40: 10rem;
|
||||||
|
--size-48: 12rem;
|
||||||
|
--size-64: 16rem;
|
||||||
|
--size-80: 20rem;
|
||||||
|
--size-96: 24rem;
|
||||||
|
--size-128: 32rem;
|
||||||
|
--size-160: 40rem;
|
||||||
|
--size-192: 48rem;
|
||||||
|
--size-256: 64rem;
|
||||||
|
--size-320: 80rem;
|
||||||
|
|
||||||
|
/* === Semantic Spacing === */
|
||||||
|
--spacing-none: var(--size-0);
|
||||||
|
--spacing-hairline: var(--size-px);
|
||||||
|
--spacing-tight: var(--size-1);
|
||||||
|
--spacing-snug: var(--size-2);
|
||||||
|
--spacing-cozy: var(--size-4);
|
||||||
|
--spacing-comfortable: var(--size-6);
|
||||||
|
--spacing-relaxed: var(--size-8);
|
||||||
|
--spacing-spacious: var(--size-12);
|
||||||
|
--spacing-generous: var(--size-16);
|
||||||
|
--spacing-luxurious: var(--size-24);
|
||||||
|
--spacing-expansive: var(--size-32);
|
||||||
|
}
|
||||||
132
src/styles/elements.css
Normal file
132
src/styles/elements.css
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
:root {
|
||||||
|
/* === Heading Colors & Font Sizes === */
|
||||||
|
/* These remain variables because they reference semantic tokens */
|
||||||
|
|
||||||
|
--el-h1-color: var(--color-text-primary);
|
||||||
|
--el-h1-font-family: var(--font-display);
|
||||||
|
--el-h1-font-size: var(--typo-size-7xl);
|
||||||
|
|
||||||
|
--el-h2-color: var(--color-text-primary);
|
||||||
|
--el-h2-font-family: var(--font-header);
|
||||||
|
--el-h2-font-size: var(--typo-size-5xl);
|
||||||
|
|
||||||
|
--el-h3-color: var(--color-text-secondary);
|
||||||
|
--el-h3-font-family: var(--font-header);
|
||||||
|
--el-h3-font-size: var(--typo-size-4xl);
|
||||||
|
|
||||||
|
--el-h4-color: var(--color-text-secondary);
|
||||||
|
--el-h4-font-family: var(--font-header);
|
||||||
|
--el-h4-font-size: var(--typo-size-3xl);
|
||||||
|
|
||||||
|
--el-h5-color: var(--color-text-secondary);
|
||||||
|
--el-h5-font-family: var(--font-header);
|
||||||
|
--el-h5-font-size: var(--typo-size-2xl);
|
||||||
|
|
||||||
|
--el-h6-color: var(--color-text-secondary);
|
||||||
|
--el-h6-font-family: var(--font-header);
|
||||||
|
--el-h6-font-size: var(--typo-size-xl);
|
||||||
|
|
||||||
|
/* === Heading Vertical Spacing === */
|
||||||
|
--el-h1-vspace-base: calc(1em * 1.125);
|
||||||
|
--el-h1-vspace-top: calc(var(--el-h1-vspace-base) * var(--vspace-spacious));
|
||||||
|
--el-h1-vspace-bottom: calc(
|
||||||
|
var(--el-h1-vspace-base) * var(--vspace-comfortable)
|
||||||
|
);
|
||||||
|
|
||||||
|
--el-h2-vspace-base: calc(1em * 1.1765);
|
||||||
|
--el-h2-vspace-top: calc(var(--el-h2-vspace-base) * var(--vspace-snug));
|
||||||
|
--el-h2-vspace-bottom: calc(
|
||||||
|
var(--el-h2-vspace-base) * var(--vspace-compressed)
|
||||||
|
);
|
||||||
|
|
||||||
|
--el-h3-vspace-base: calc(1em * 1.2);
|
||||||
|
--el-h3-vspace-top: calc(var(--el-h3-vspace-base) * var(--vspace-cozy));
|
||||||
|
--el-h3-vspace-bottom: calc(var(--el-h3-vspace-base) * var(--vspace-snug));
|
||||||
|
|
||||||
|
--el-h4-vspace-base: calc(1em * 1.125);
|
||||||
|
--el-h4-vspace-top: calc(var(--el-h4-vspace-base) * var(--vspace-normal));
|
||||||
|
--el-h4-vspace-bottom: calc(var(--el-h4-vspace-base) * var(--vspace-tight));
|
||||||
|
|
||||||
|
--el-h5-vspace-base: calc(1em * 1.28);
|
||||||
|
--el-h5-vspace-top: calc(var(--el-h5-vspace-base) * var(--vspace-cozy));
|
||||||
|
--el-h5-vspace-bottom: calc(var(--el-h5-vspace-base) * var(--vspace-tight));
|
||||||
|
|
||||||
|
--el-h6-vspace-base: calc(1em * 1.4);
|
||||||
|
--el-h6-vspace-top: calc(var(--el-h6-vspace-base) * var(--vspace-snug));
|
||||||
|
--el-h6-vspace-bottom: calc(
|
||||||
|
var(--el-h6-vspace-base) * var(--vspace-compressed)
|
||||||
|
);
|
||||||
|
|
||||||
|
/* === Body Text === */
|
||||||
|
--el-p-color: var(--color-text-primary);
|
||||||
|
--el-p-font-family: var(--font-body);
|
||||||
|
--el-p-font-size: var(--typo-size-md);
|
||||||
|
--el-p-vspace-base: calc(1em * var(--typo-leading-normal));
|
||||||
|
--el-p-vspace-top: calc(var(--el-p-vspace-base) * var(--vspace-snug));
|
||||||
|
--el-p-vspace-bottom: calc(var(--el-p-vspace-base) * var(--vspace-snug));
|
||||||
|
|
||||||
|
/* === Blockquote === */
|
||||||
|
--el-blockquote-color: var(--color-text-tertiary);
|
||||||
|
--el-blockquote-font-family: var(--font-body);
|
||||||
|
--el-blockquote-font-size: var(--typo-size-md);
|
||||||
|
--el-blockquote-vspace-base: calc(1em * var(--typo-leading-comfortable));
|
||||||
|
--el-blockquote-vspace-top: calc(
|
||||||
|
var(--el-blockquote-vspace-base) * var(--vspace-snug)
|
||||||
|
);
|
||||||
|
--el-blockquote-vspace-bottom: calc(
|
||||||
|
var(--el-blockquote-vspace-base) * var(--vspace-snug)
|
||||||
|
);
|
||||||
|
|
||||||
|
/* === Pre / Code === */
|
||||||
|
--el-pre-color: var(--color-text-inverse);
|
||||||
|
--el-pre-background: var(--color-surface-inverse);
|
||||||
|
--el-pre-font-family: var(--font-mono);
|
||||||
|
--el-pre-font-size: var(--typo-size-sm);
|
||||||
|
--el-pre-vspace-base: calc(1em * 1.5385);
|
||||||
|
--el-pre-vspace-top: calc(var(--el-pre-vspace-base) * var(--vspace-normal));
|
||||||
|
--el-pre-vspace-bottom: calc(
|
||||||
|
var(--el-pre-vspace-base) * var(--vspace-normal)
|
||||||
|
);
|
||||||
|
|
||||||
|
/* === Lists === */
|
||||||
|
--el-list-color: var(--color-text-primary);
|
||||||
|
--el-list-font-size: var(--typo-size-md);
|
||||||
|
--el-list-vspace-base: calc(1em * var(--typo-leading-normal));
|
||||||
|
--el-list-vspace-top: calc(var(--el-list-vspace-base) * var(--vspace-snug));
|
||||||
|
--el-list-vspace-bottom: calc(
|
||||||
|
var(--el-list-vspace-base) * var(--vspace-snug)
|
||||||
|
);
|
||||||
|
--el-list-nested-vspace-top: calc(
|
||||||
|
var(--el-list-vspace-base) * var(--vspace-snug)
|
||||||
|
);
|
||||||
|
--el-list-nested-vspace-bottom: calc(
|
||||||
|
var(--el-list-vspace-base) * var(--vspace-snug)
|
||||||
|
);
|
||||||
|
--el-li-vspace-top: calc(var(--el-list-vspace-base) * var(--vspace-snug));
|
||||||
|
--el-li-vspace-bottom: calc(var(--el-list-vspace-base) * var(--vspace-snug));
|
||||||
|
--el-li-nested-vspace-top: calc(
|
||||||
|
var(--el-list-vspace-base) * var(--vspace-tight)
|
||||||
|
);
|
||||||
|
--el-li-nested-vspace-bottom: calc(
|
||||||
|
var(--el-list-vspace-base) * var(--vspace-tight)
|
||||||
|
);
|
||||||
|
|
||||||
|
/* === Table === */
|
||||||
|
--el-th-color: var(--color-text-inverse);
|
||||||
|
--el-th-background: var(--color-surface-inverse);
|
||||||
|
--el-th-font-family: var(--font-header);
|
||||||
|
--el-td-color: var(--color-text-primary);
|
||||||
|
--el-td-font-family: var(--font-mono);
|
||||||
|
|
||||||
|
/* === DL/DT/DD === */
|
||||||
|
--el-dt-color: var(--color-text-inverse);
|
||||||
|
--el-dt-background: var(--color-surface-inverse);
|
||||||
|
--el-dt-font-family: var(--font-header);
|
||||||
|
--el-dd-color: var(--color-text-primary);
|
||||||
|
--el-dd-font-family: var(--font-body);
|
||||||
|
|
||||||
|
/* === HR === */
|
||||||
|
--hr-color: var(--color-text-tertiary);
|
||||||
|
--hr-symbol-color: var(--color-text-tertiary);
|
||||||
|
--hr-symbol-background: var(--color-surface-base);
|
||||||
|
}
|
||||||
11
src/styles/fonts.css
Normal file
11
src/styles/fonts.css
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
@import '@fontsource/blaka';
|
||||||
|
@import '@fontsource-variable/geist-mono';
|
||||||
|
@import '@fontsource-variable/geist';
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Unigrim Dee';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: url('../fonts/UnigrimDee-Regular.woff2') format('woff2');
|
||||||
|
}
|
||||||
373
src/styles/foundation.css
vendored
Normal file
373
src/styles/foundation.css
vendored
Normal file
@@ -0,0 +1,373 @@
|
|||||||
|
@layer reset {
|
||||||
|
/*
|
||||||
|
Foundation inspired by Tailwind CSS Preflight. (https://tailwindcss.com/docs/preflight)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
|
||||||
|
2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
|
||||||
|
*/
|
||||||
|
|
||||||
|
*,
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
border-style: solid; /* 2 */
|
||||||
|
border-width: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
1. Use a consistent sensible line-height in all browsers.
|
||||||
|
2. Prevent adjustments of font size after orientation changes in iOS.
|
||||||
|
3. Use a more readable tab size.
|
||||||
|
4. Use the system's `sans` font-family by default.
|
||||||
|
*/
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
||||||
|
"Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif,
|
||||||
|
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 4 */
|
||||||
|
|
||||||
|
line-height: 1.5; /* 1 */
|
||||||
|
text-size-adjust: 100%; /* 2 */
|
||||||
|
tab-size: 4; /* 3 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
1. Remove the margin in all browsers.
|
||||||
|
2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
|
||||||
|
*/
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0; /* 1 */
|
||||||
|
line-height: inherit; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
1. Add the correct height in Firefox.
|
||||||
|
2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
|
||||||
|
3. Ensure horizontal rules are visible by default.
|
||||||
|
*/
|
||||||
|
|
||||||
|
hr {
|
||||||
|
height: 0; /* 1 */
|
||||||
|
border-top-width: 1px; /* 3 */
|
||||||
|
color: inherit; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Add the correct text decoration in Chrome, Edge, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abbr:where([title]) {
|
||||||
|
text-decoration: underline dotted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Remove the default font size and weight for headings.
|
||||||
|
*/
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
font-size: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Reset links to optimize for opt-in styling instead of opt-out.
|
||||||
|
*/
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Add the correct font weight in Edge and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
1. Use the system's `mono` font family by default.
|
||||||
|
2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp,
|
||||||
|
pre {
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||||
|
"Liberation Mono", "Courier New", monospace; /* 1 */
|
||||||
|
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Add the correct font size in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Prevent `sub` and `sup` elements from affecting the line height in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
position: relative;
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
|
||||||
|
2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
|
||||||
|
3. Remove gaps between table borders by default.
|
||||||
|
*/
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse; /* 3 */
|
||||||
|
border-color: inherit; /* 2 */
|
||||||
|
text-indent: 0; /* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
1. Change the font styles in all browsers.
|
||||||
|
2. Remove the margin in Firefox and Safari.
|
||||||
|
3. Remove default padding in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
margin: 0; /* 2 */
|
||||||
|
padding: 0; /* 3 */
|
||||||
|
|
||||||
|
font-family: inherit; /* 1 */
|
||||||
|
font-size: 100%; /* 1 */
|
||||||
|
font-weight: inherit; /* 1 */
|
||||||
|
line-height: inherit; /* 1 */
|
||||||
|
color: inherit; /* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Remove the inheritance of text transform in Edge and Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
1. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
2. Remove default button styles.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
[type="button"],
|
||||||
|
[type="reset"],
|
||||||
|
[type="submit"] {
|
||||||
|
appearance: auto; /* 1 */
|
||||||
|
background-color: transparent; /* 2 */
|
||||||
|
background-image: none; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Use the modern Firefox focus style for all focusable elements.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:-moz-focusring {
|
||||||
|
outline: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
|
||||||
|
*/
|
||||||
|
|
||||||
|
:-moz-ui-invalid {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Add the correct vertical alignment in Chrome and Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Correct the cursor style of increment and decrement buttons in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
::-webkit-inner-spin-button,
|
||||||
|
::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
1. Correct the odd appearance in Chrome and Safari.
|
||||||
|
2. Correct the outline style in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"] {
|
||||||
|
appearance: textfield; /* 1 */
|
||||||
|
outline-offset: -2px; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Remove the inner padding in Chrome and Safari on macOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
::-webkit-search-decoration {
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
1. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
2. Change font properties to `inherit` in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
font: inherit; /* 2 */
|
||||||
|
appearance: auto; /* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Add the correct display in Chrome and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Removes the default spacing and border for appropriate elements.
|
||||||
|
*/
|
||||||
|
|
||||||
|
blockquote,
|
||||||
|
dl,
|
||||||
|
dd,
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6,
|
||||||
|
hr,
|
||||||
|
figure,
|
||||||
|
p,
|
||||||
|
pre {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol,
|
||||||
|
ul,
|
||||||
|
menu {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Prevent resizing textareas horizontally by default.
|
||||||
|
*/
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
|
||||||
|
*/
|
||||||
|
|
||||||
|
input::placeholder,
|
||||||
|
textarea::placeholder {
|
||||||
|
opacity: 1; /* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Set the default cursor for buttons.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
[role="button"] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Make sure disabled buttons don't get the pointer cursor.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:disabled {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
|
||||||
|
2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
|
||||||
|
This can trigger a poorly considered lint error in some tools but is included by design.
|
||||||
|
*/
|
||||||
|
|
||||||
|
img,
|
||||||
|
svg,
|
||||||
|
video,
|
||||||
|
canvas,
|
||||||
|
audio,
|
||||||
|
iframe,
|
||||||
|
embed,
|
||||||
|
object {
|
||||||
|
display: block; /* 1 */
|
||||||
|
vertical-align: middle; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
|
||||||
|
*/
|
||||||
|
|
||||||
|
img,
|
||||||
|
video {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Make elements with the HTML hidden attribute stay hidden by default.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
58
src/styles/typography.css
Normal file
58
src/styles/typography.css
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
:root {
|
||||||
|
/* === Font Families === */
|
||||||
|
--font-header: var(--font-geist-sans);
|
||||||
|
--font-display: var(--font-blaka);
|
||||||
|
--font-body: var(--font-geist-mono);
|
||||||
|
--font-mono: var(--font-geist-mono);
|
||||||
|
--font-symbols: var();
|
||||||
|
|
||||||
|
/* === Type Scale === */
|
||||||
|
--typo-size-responsive: clamp(1rem, 2.5vw, 1.25rem);
|
||||||
|
--typo-size-base: 16px;
|
||||||
|
--typo-size-8xl: 8em;
|
||||||
|
--typo-size-7xl: 6.375em;
|
||||||
|
--typo-size-6xl: 5.0625em;
|
||||||
|
--typo-size-5xl: 4em;
|
||||||
|
--typo-size-4xl: 3.1875em;
|
||||||
|
--typo-size-3xl: 2.5em;
|
||||||
|
--typo-size-2xl: 2em;
|
||||||
|
--typo-size-xl: 1.5625em;
|
||||||
|
--typo-size-lg: 1.25em;
|
||||||
|
--typo-size-md: 1em;
|
||||||
|
--typo-size-sm: 0.8125em;
|
||||||
|
--typo-size-xs: 0.625em;
|
||||||
|
--typo-size-2xs: 0.5em;
|
||||||
|
|
||||||
|
/* === Font Weights === */
|
||||||
|
--typo-weight-thin: 100;
|
||||||
|
--typo-weight-extralight: 200;
|
||||||
|
--typo-weight-light: 300;
|
||||||
|
--typo-weight-normal: 400;
|
||||||
|
--typo-weight-medium: 500;
|
||||||
|
--typo-weight-semibold: 600;
|
||||||
|
--typo-weight-bold: 700;
|
||||||
|
--typo-weight-extrabold: 800;
|
||||||
|
--typo-weight-black: 900;
|
||||||
|
|
||||||
|
/* === Line Height === */
|
||||||
|
--typo-leading-compressed: 1;
|
||||||
|
--typo-leading-tight: 1.125;
|
||||||
|
--typo-leading-snug: 1.25;
|
||||||
|
--typo-leading-cozy: 1.375;
|
||||||
|
--typo-leading-normal: 1.5;
|
||||||
|
--typo-leading-relaxed: 1.625;
|
||||||
|
--typo-leading-comfortable: 1.75;
|
||||||
|
--typo-leading-loose: 1.875;
|
||||||
|
--typo-leading-spacious: 2;
|
||||||
|
|
||||||
|
/* === Vertical Spacing Multipliers === */
|
||||||
|
--vspace-compressed: 0.25;
|
||||||
|
--vspace-tight: 0.5;
|
||||||
|
--vspace-snug: 0.75;
|
||||||
|
--vspace-cozy: 1;
|
||||||
|
--vspace-normal: 1.25;
|
||||||
|
--vspace-relaxed: 1.5;
|
||||||
|
--vspace-comfortable: 1.75;
|
||||||
|
--vspace-loose: 2;
|
||||||
|
--vspace-spacious: 2.5;
|
||||||
|
}
|
||||||
56
stylelint.config.mjs
Normal file
56
stylelint.config.mjs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
const stylelintConfig = {
|
||||||
|
extends: [
|
||||||
|
'stylelint-config-standard',
|
||||||
|
'stylelint-config-clean-order',
|
||||||
|
'stylelint-config-html',
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
'declaration-block-no-redundant-longhand-properties': null,
|
||||||
|
'declaration-property-value-no-unknown': [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
ignoreProperties: {
|
||||||
|
top: [/^anchor\(/],
|
||||||
|
left: [/^anchor\(/],
|
||||||
|
right: [/^anchor\(/],
|
||||||
|
bottom: [/^anchor\(/],
|
||||||
|
inset: [/^anchor\(/],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'no-descending-specificity': null,
|
||||||
|
'selector-class-pattern': null,
|
||||||
|
'custom-property-pattern': null,
|
||||||
|
'custom-property-no-missing-var-function': [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
ignore: [
|
||||||
|
'--font-iosevka-mono',
|
||||||
|
'--font-geist-sans',
|
||||||
|
'--font-iosevka-slab',
|
||||||
|
'--font-blaka',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'at-rule-no-unknown': [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
ignoreAtRules: ['custom-media', 'define-mixin', 'mixin'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'function-no-unknown': [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
ignoreFunctions: ['/^\\$[a-zA-Z][\\w-]*$/'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'value-keyword-case': [
|
||||||
|
'lower',
|
||||||
|
{
|
||||||
|
ignoreKeywords: ['/^\\$[a-zA-Z][\\w-]*$/'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default stylelintConfig;
|
||||||
Reference in New Issue
Block a user