@markuxt/markuxt 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +168 -0
- package/app.config.d.ts +33 -0
- package/nuxt.config.ts +170 -0
- package/package.json +43 -0
- package/src/components/AppFooter.vue +225 -0
- package/src/components/AppHeader.vue +342 -0
- package/src/components/Hero.vue +438 -0
- package/src/components/Icon.vue +131 -0
- package/src/components/LanguageSwitcher.vue +71 -0
- package/src/components/MemberCard.vue +198 -0
- package/src/components/MembersGrid.vue +129 -0
- package/src/components/MermaidDiagram.vue +99 -0
- package/src/components/NewsCard.vue +119 -0
- package/src/components/PublicationCard.vue +245 -0
- package/src/components/SectionTitle.vue +75 -0
- package/src/components/content/ProseImg.vue +29 -0
- package/src/components/content/ProsePre.vue +58 -0
- package/src/components/content/ProseVideo.vue +45 -0
- package/src/composables/resolveContentImage.ts +35 -0
- package/src/content-transformers/binary-assets.ts +20 -0
- package/src/error.vue +58 -0
- package/src/layouts/default.vue +37 -0
- package/src/middleware/navigation-guard.ts +22 -0
- package/src/pages/index.vue +232 -0
- package/src/pages/members/[...slug].vue +542 -0
- package/src/pages/members/index.vue +147 -0
- package/src/pages/news/[...slug].vue +280 -0
- package/src/pages/news/index.vue +102 -0
- package/src/pages/positions/[...slug].vue +425 -0
- package/src/pages/positions/index.vue +266 -0
- package/src/pages/projects/[...slug].vue +441 -0
- package/src/pages/projects/index.vue +499 -0
- package/src/pages/publications/[...slug].vue +435 -0
- package/src/pages/publications/index.vue +145 -0
- package/src/plugins/mathml-components.ts +33 -0
- package/src/plugins/suppress-warnings.ts +40 -0
- package/src/public/_markuxt/components/AppFooter.vue +225 -0
- package/src/public/_markuxt/components/AppHeader.vue +342 -0
- package/src/public/_markuxt/components/Hero.vue +430 -0
- package/src/public/_markuxt/components/Icon.vue +131 -0
- package/src/public/_markuxt/components/LanguageSwitcher.vue +71 -0
- package/src/public/_markuxt/components/MemberCard.vue +198 -0
- package/src/public/_markuxt/components/MembersGrid.vue +129 -0
- package/src/public/_markuxt/components/MermaidDiagram.vue +99 -0
- package/src/public/_markuxt/components/NewsCard.vue +119 -0
- package/src/public/_markuxt/components/PublicationCard.vue +245 -0
- package/src/public/_markuxt/components/SectionTitle.vue +75 -0
- package/src/public/_markuxt/components/content/ProseImg.vue +29 -0
- package/src/public/_markuxt/components/content/ProsePre.vue +58 -0
- package/src/public/_markuxt/components/content/ProseVideo.vue +45 -0
- package/src/public/_markuxt/composables/resolveContentImage.ts +35 -0
- package/src/public/_markuxt/content-transformers/binary-assets.ts +20 -0
- package/src/public/_markuxt/error.vue +58 -0
- package/src/public/_markuxt/layouts/default.vue +37 -0
- package/src/public/_markuxt/middleware/navigation-guard.ts +22 -0
- package/src/public/_markuxt/pages/index.vue +232 -0
- package/src/public/_markuxt/pages/members/[...slug].vue +542 -0
- package/src/public/_markuxt/pages/members/index.vue +147 -0
- package/src/public/_markuxt/pages/news/[...slug].vue +280 -0
- package/src/public/_markuxt/pages/news/index.vue +102 -0
- package/src/public/_markuxt/pages/positions/[...slug].vue +425 -0
- package/src/public/_markuxt/pages/positions/index.vue +266 -0
- package/src/public/_markuxt/pages/projects/[...slug].vue +441 -0
- package/src/public/_markuxt/pages/projects/index.vue +499 -0
- package/src/public/_markuxt/pages/publications/[...slug].vue +435 -0
- package/src/public/_markuxt/pages/publications/index.vue +145 -0
- package/src/public/_markuxt/plugins/mathml-components.ts +33 -0
- package/src/public/_markuxt/plugins/suppress-warnings.ts +40 -0
- package/src/public/_markuxt/server/plugins/content-locale.ts +47 -0
- package/src/public/_markuxt/server/plugins/fix-content-anchors.ts +63 -0
- package/src/public/_markuxt/styles/_animations.css +99 -0
- package/src/public/_markuxt/styles/_base.css +31 -0
- package/src/public/_markuxt/styles/_code.css +109 -0
- package/src/public/_markuxt/styles/_components.css +109 -0
- package/src/public/_markuxt/styles/_layout.css +220 -0
- package/src/public/_markuxt/styles/_markdown.css +52 -0
- package/src/public/_markuxt/styles/_tokens.css +62 -0
- package/src/public/_markuxt/styles/_typography.css +144 -0
- package/src/public/_markuxt/styles/_utilities.css +110 -0
- package/src/public/_markuxt/styles/main.css +784 -0
- package/src/public/images/logo.png +0 -0
- package/src/server/plugins/content-locale.ts +47 -0
- package/src/server/plugins/fix-content-anchors.ts +63 -0
- package/src/styles/_animations.css +99 -0
- package/src/styles/_base.css +31 -0
- package/src/styles/_code.css +109 -0
- package/src/styles/_components.css +109 -0
- package/src/styles/_layout.css +220 -0
- package/src/styles/_markdown.css +52 -0
- package/src/styles/_tokens.css +62 -0
- package/src/styles/_typography.css +144 -0
- package/src/styles/_utilities.css +110 -0
- package/src/styles/main.css +784 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Fix in-page anchor links for content pasted from GitHub READMEs.
|
|
2
|
+
//
|
|
3
|
+
// Background: when a heading like `## 🧭 Start Here` is parsed, github-slugger
|
|
4
|
+
// produces `-start-here` (emoji dropped, leading dash kept). GitHub uses that
|
|
5
|
+
// exact string as the heading's anchor, so hand-written tables of contents link
|
|
6
|
+
// to `#-start-here`. Nuxt Content's MDC compiler, however, runs one extra step
|
|
7
|
+
// GitHub does not — it strips the leading/trailing dashes — so the heading's id
|
|
8
|
+
// becomes `start-here`. The result: every emoji-prefixed TOC link 404s in-page.
|
|
9
|
+
//
|
|
10
|
+
// Rather than rewrite the markdown (which is correct GitHub syntax), we
|
|
11
|
+
// normalize the anchor hrefs at parse time using the SAME rule MDC applies to
|
|
12
|
+
// heading ids, so the links converge onto the generated ids. Idempotent, and we
|
|
13
|
+
// only rewrite a link when doing so actually points it at a real heading.
|
|
14
|
+
|
|
15
|
+
// Mirror of MDC's heading-id tail normalization (compiler.js):
|
|
16
|
+
// slug(text).replace(/-+/g, '-').replace(/^-|-$/g, '').replace(/^(\d)/, '_$1')
|
|
17
|
+
function normalizeId(fragment: string): string {
|
|
18
|
+
return fragment
|
|
19
|
+
.replace(/-+/g, '-')
|
|
20
|
+
.replace(/^-|-$/g, '')
|
|
21
|
+
.replace(/^(\d)/, '_$1')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Collect ids of all heading nodes (h1–h6) in the parsed body AST.
|
|
25
|
+
function collectHeadingIds(node: any, ids: Set<string>): void {
|
|
26
|
+
if (!node || typeof node !== 'object') return
|
|
27
|
+
if (Array.isArray(node)) {
|
|
28
|
+
for (const child of node) collectHeadingIds(child, ids)
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
if (typeof node.tag === 'string' && /^h[1-6]$/.test(node.tag) && node.props?.id) {
|
|
32
|
+
ids.add(node.props.id)
|
|
33
|
+
}
|
|
34
|
+
if (node.children) collectHeadingIds(node.children, ids)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Rewrite in-page anchor hrefs that only match a heading after normalization.
|
|
38
|
+
function fixAnchors(node: any, ids: Set<string>): void {
|
|
39
|
+
if (!node || typeof node !== 'object') return
|
|
40
|
+
if (Array.isArray(node)) {
|
|
41
|
+
for (const child of node) fixAnchors(child, ids)
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
if (node.tag === 'a' && typeof node.props?.href === 'string' && node.props.href.startsWith('#')) {
|
|
45
|
+
const fragment = node.props.href.slice(1)
|
|
46
|
+
if (fragment && !ids.has(fragment)) {
|
|
47
|
+
const normalized = normalizeId(fragment)
|
|
48
|
+
if (ids.has(normalized)) {
|
|
49
|
+
node.props.href = '#' + normalized
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (node.children) fixAnchors(node.children, ids)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default defineNitroPlugin((nitroApp) => {
|
|
57
|
+
nitroApp.hooks.hook('content:file:afterParse', (file: any) => {
|
|
58
|
+
if (!file?._id?.endsWith('.md') || !file.body?.children) return
|
|
59
|
+
const ids = new Set<string>()
|
|
60
|
+
collectHeadingIds(file.body, ids)
|
|
61
|
+
if (ids.size) fixAnchors(file.body, ids)
|
|
62
|
+
})
|
|
63
|
+
})
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
Animation Classes
|
|
3
|
+
============================================ */
|
|
4
|
+
|
|
5
|
+
@keyframes fadeInUp {
|
|
6
|
+
from {
|
|
7
|
+
opacity: 0;
|
|
8
|
+
transform: translateY(20px);
|
|
9
|
+
}
|
|
10
|
+
to {
|
|
11
|
+
opacity: 1;
|
|
12
|
+
transform: translateY(0);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@keyframes fadeIn {
|
|
17
|
+
from {
|
|
18
|
+
opacity: 0;
|
|
19
|
+
}
|
|
20
|
+
to {
|
|
21
|
+
opacity: 1;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@keyframes slideInLeft {
|
|
26
|
+
from {
|
|
27
|
+
opacity: 0;
|
|
28
|
+
transform: translateX(-30px);
|
|
29
|
+
}
|
|
30
|
+
to {
|
|
31
|
+
opacity: 1;
|
|
32
|
+
transform: translateX(0);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@keyframes slideInRight {
|
|
37
|
+
from {
|
|
38
|
+
opacity: 0;
|
|
39
|
+
transform: translateX(30px);
|
|
40
|
+
}
|
|
41
|
+
to {
|
|
42
|
+
opacity: 1;
|
|
43
|
+
transform: translateX(0);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.animate-fade-in-up {
|
|
48
|
+
animation: fadeInUp 0.6s ease forwards;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.animate-fade-in {
|
|
52
|
+
animation: fadeIn 0.4s ease forwards;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.animate-slide-left {
|
|
56
|
+
animation: slideInLeft 0.5s ease forwards;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.animate-slide-right {
|
|
60
|
+
animation: slideInRight 0.5s ease forwards;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Stagger delays */
|
|
64
|
+
.delay-100 {
|
|
65
|
+
animation-delay: 0.1s;
|
|
66
|
+
}
|
|
67
|
+
.delay-200 {
|
|
68
|
+
animation-delay: 0.2s;
|
|
69
|
+
}
|
|
70
|
+
.delay-300 {
|
|
71
|
+
animation-delay: 0.3s;
|
|
72
|
+
}
|
|
73
|
+
.delay-400 {
|
|
74
|
+
animation-delay: 0.4s;
|
|
75
|
+
}
|
|
76
|
+
.delay-500 {
|
|
77
|
+
animation-delay: 0.5s;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* ============================================
|
|
81
|
+
Page Transitions
|
|
82
|
+
============================================ */
|
|
83
|
+
|
|
84
|
+
.page-enter-active,
|
|
85
|
+
.page-leave-active {
|
|
86
|
+
transition:
|
|
87
|
+
opacity 0.3s ease,
|
|
88
|
+
transform 0.3s ease;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.page-enter-from {
|
|
92
|
+
opacity: 0;
|
|
93
|
+
transform: translateY(10px);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.page-leave-to {
|
|
97
|
+
opacity: 0;
|
|
98
|
+
transform: translateY(-10px);
|
|
99
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
Base Styles
|
|
3
|
+
============================================ */
|
|
4
|
+
|
|
5
|
+
*,
|
|
6
|
+
*::before,
|
|
7
|
+
*::after {
|
|
8
|
+
box-sizing: border-box;
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
html {
|
|
14
|
+
font-size: 16px;
|
|
15
|
+
scroll-behavior: smooth;
|
|
16
|
+
-webkit-font-smoothing: antialiased;
|
|
17
|
+
-moz-osx-font-smoothing: grayscale;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
body {
|
|
21
|
+
font-family: var(--font-body);
|
|
22
|
+
font-size: 1rem;
|
|
23
|
+
line-height: 1.6;
|
|
24
|
+
color: var(--color-text);
|
|
25
|
+
background-color: var(--color-bg);
|
|
26
|
+
background-image:
|
|
27
|
+
linear-gradient(rgba(10, 37, 64, 0.02) 1px, transparent 1px),
|
|
28
|
+
linear-gradient(90deg, rgba(10, 37, 64, 0.02) 1px, transparent 1px);
|
|
29
|
+
background-size: 40px 40px;
|
|
30
|
+
min-height: 100vh;
|
|
31
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
Markdown Code Blocks (ProsePre override)
|
|
3
|
+
Light theme: very light grey surface, subtle border
|
|
4
|
+
============================================ */
|
|
5
|
+
|
|
6
|
+
/* Wrapper around a highlighted code block */
|
|
7
|
+
.code-block {
|
|
8
|
+
margin: var(--spacing-lg) 0;
|
|
9
|
+
border: 1px solid var(--color-border);
|
|
10
|
+
border-radius: var(--radius-lg);
|
|
11
|
+
overflow: hidden;
|
|
12
|
+
background: #f6f8fa;
|
|
13
|
+
box-shadow: var(--shadow-sm);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* Header strip: language label + copy button */
|
|
17
|
+
.code-block__header {
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
justify-content: space-between;
|
|
21
|
+
gap: var(--spacing-md);
|
|
22
|
+
padding: var(--spacing-xs) var(--spacing-md);
|
|
23
|
+
background: #eaeef2;
|
|
24
|
+
border-bottom: 1px solid var(--color-border);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.code-block__lang {
|
|
28
|
+
font-family: var(--font-body);
|
|
29
|
+
font-size: 0.75rem;
|
|
30
|
+
font-weight: 600;
|
|
31
|
+
letter-spacing: 0.04em;
|
|
32
|
+
text-transform: uppercase;
|
|
33
|
+
color: var(--color-text-muted);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.code-block__copy {
|
|
37
|
+
display: inline-flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
justify-content: center;
|
|
40
|
+
padding: 0.3rem;
|
|
41
|
+
color: var(--color-text-muted);
|
|
42
|
+
background: transparent;
|
|
43
|
+
border: none;
|
|
44
|
+
border-radius: var(--radius-sm);
|
|
45
|
+
cursor: pointer;
|
|
46
|
+
transition: all var(--transition-fast);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.code-block__copy:hover {
|
|
50
|
+
background: rgba(0, 0, 0, 0.06);
|
|
51
|
+
color: var(--color-text);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.code-block__copy.is-copied {
|
|
55
|
+
color: #1a7f37;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* The <pre> emitted inside our wrapper: reset chrome, keep scrolling */
|
|
59
|
+
.code-block pre {
|
|
60
|
+
margin: 0;
|
|
61
|
+
padding: var(--spacing-md) var(--spacing-lg);
|
|
62
|
+
overflow-x: auto;
|
|
63
|
+
background: transparent;
|
|
64
|
+
font-size: 0.875rem;
|
|
65
|
+
line-height: 1.6;
|
|
66
|
+
-webkit-overflow-scrolling: touch;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.code-block pre code {
|
|
70
|
+
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Fira Code', Menlo, Consolas, monospace;
|
|
71
|
+
font-size: inherit;
|
|
72
|
+
color: var(--color-text);
|
|
73
|
+
background: none;
|
|
74
|
+
padding: 0;
|
|
75
|
+
display: block;
|
|
76
|
+
counter-reset: line;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.code-block pre code .line {
|
|
80
|
+
display: block;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* Line-number gutter: rendered from a CSS counter so the digits are never
|
|
84
|
+
part of the copied text (manual selection skips ::before; the copy button
|
|
85
|
+
copies the raw source). Sticky so numbers stay pinned while a long line
|
|
86
|
+
scrolls horizontally, GitHub-style. */
|
|
87
|
+
.code-block pre code .line::before {
|
|
88
|
+
counter-increment: line;
|
|
89
|
+
content: counter(line);
|
|
90
|
+
position: sticky;
|
|
91
|
+
left: 0;
|
|
92
|
+
display: inline-block;
|
|
93
|
+
width: 2.5ch;
|
|
94
|
+
margin-right: var(--spacing-md);
|
|
95
|
+
padding-right: var(--spacing-sm);
|
|
96
|
+
text-align: right;
|
|
97
|
+
color: var(--color-text-muted);
|
|
98
|
+
background: #f6f8fa;
|
|
99
|
+
border-right: 1px solid var(--color-border);
|
|
100
|
+
user-select: none;
|
|
101
|
+
-webkit-user-select: none;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Shiki dual-theme: the parser writes both colors as CSS vars
|
|
105
|
+
(--shiki-default = github-light, --shiki-dusk = github-dark) rather than a
|
|
106
|
+
plain `color`. We render on a light surface, so use the light-theme var. */
|
|
107
|
+
.code-block pre code span {
|
|
108
|
+
color: var(--shiki-default, inherit);
|
|
109
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
Components
|
|
3
|
+
============================================ */
|
|
4
|
+
|
|
5
|
+
/* IconPark / inline icon helper */
|
|
6
|
+
.icon-inline {
|
|
7
|
+
display: inline-flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
justify-content: center;
|
|
10
|
+
vertical-align: middle;
|
|
11
|
+
}
|
|
12
|
+
.icon-inline svg {
|
|
13
|
+
display: block;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* Buttons */
|
|
17
|
+
.btn {
|
|
18
|
+
display: inline-flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
gap: var(--spacing-sm);
|
|
22
|
+
padding: var(--spacing-sm) var(--spacing-lg);
|
|
23
|
+
font-family: var(--font-body);
|
|
24
|
+
font-size: 0.875rem;
|
|
25
|
+
font-weight: 600;
|
|
26
|
+
line-height: 1;
|
|
27
|
+
text-align: center;
|
|
28
|
+
text-decoration: none;
|
|
29
|
+
border: none;
|
|
30
|
+
border-radius: var(--radius-md);
|
|
31
|
+
cursor: pointer;
|
|
32
|
+
transition: all var(--transition-base);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.btn-primary {
|
|
36
|
+
background: linear-gradient(
|
|
37
|
+
135deg,
|
|
38
|
+
var(--color-accent) 0%,
|
|
39
|
+
var(--color-secondary) 100%
|
|
40
|
+
);
|
|
41
|
+
color: white;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.btn-primary:hover {
|
|
45
|
+
transform: translateY(-2px);
|
|
46
|
+
box-shadow: 0 10px 20px -5px rgba(0, 217, 255, 0.4);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.btn-secondary {
|
|
50
|
+
background-color: transparent;
|
|
51
|
+
color: var(--color-primary);
|
|
52
|
+
border: 2px solid var(--color-primary);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.btn-secondary:hover {
|
|
56
|
+
background-color: var(--color-primary);
|
|
57
|
+
color: white;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* Cards */
|
|
61
|
+
.card {
|
|
62
|
+
background: var(--color-bg-alt);
|
|
63
|
+
border-radius: var(--radius-xl);
|
|
64
|
+
padding: var(--spacing-xl);
|
|
65
|
+
box-shadow: var(--shadow-md);
|
|
66
|
+
border: 1px solid var(--color-border);
|
|
67
|
+
transition: all var(--transition-base);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.card:hover {
|
|
71
|
+
transform: translateY(-4px);
|
|
72
|
+
box-shadow: var(--shadow-xl);
|
|
73
|
+
border-color: var(--color-secondary);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* Badge */
|
|
77
|
+
.badge {
|
|
78
|
+
display: inline-flex;
|
|
79
|
+
align-items: center;
|
|
80
|
+
padding: var(--spacing-xs) var(--spacing-sm);
|
|
81
|
+
font-size: 0.75rem;
|
|
82
|
+
font-weight: 600;
|
|
83
|
+
line-height: 1;
|
|
84
|
+
border-radius: var(--radius-full);
|
|
85
|
+
background-color: var(--color-secondary);
|
|
86
|
+
color: white;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.badge-accent {
|
|
90
|
+
background-color: var(--color-accent);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.badge-muted {
|
|
94
|
+
background-color: var(--color-border);
|
|
95
|
+
color: var(--color-text-muted);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* Divider */
|
|
99
|
+
.divider {
|
|
100
|
+
width: 100%;
|
|
101
|
+
height: 2px;
|
|
102
|
+
background: linear-gradient(
|
|
103
|
+
90deg,
|
|
104
|
+
transparent,
|
|
105
|
+
var(--color-border),
|
|
106
|
+
transparent
|
|
107
|
+
);
|
|
108
|
+
margin: var(--spacing-2xl) 0;
|
|
109
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
Base Styles
|
|
3
|
+
============================================ */
|
|
4
|
+
|
|
5
|
+
*,
|
|
6
|
+
*::before,
|
|
7
|
+
*::after {
|
|
8
|
+
box-sizing: border-box;
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
html {
|
|
14
|
+
font-size: 16px;
|
|
15
|
+
scroll-behavior: smooth;
|
|
16
|
+
-webkit-font-smoothing: antialiased;
|
|
17
|
+
-moz-osx-font-smoothing: grayscale;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
body {
|
|
21
|
+
font-family: var(--font-body);
|
|
22
|
+
font-size: 1rem;
|
|
23
|
+
line-height: 1.6;
|
|
24
|
+
color: var(--color-text);
|
|
25
|
+
background-color: var(--color-bg);
|
|
26
|
+
background-image:
|
|
27
|
+
linear-gradient(rgba(10, 37, 64, 0.02) 1px, transparent 1px),
|
|
28
|
+
linear-gradient(90deg, rgba(10, 37, 64, 0.02) 1px, transparent 1px);
|
|
29
|
+
background-size: 40px 40px;
|
|
30
|
+
min-height: 100vh;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* ============================================
|
|
34
|
+
Typography
|
|
35
|
+
============================================ */
|
|
36
|
+
|
|
37
|
+
h1,
|
|
38
|
+
h2,
|
|
39
|
+
h3,
|
|
40
|
+
h4,
|
|
41
|
+
h5,
|
|
42
|
+
h6 {
|
|
43
|
+
font-family: var(--font-display);
|
|
44
|
+
font-weight: 700;
|
|
45
|
+
line-height: 1.2;
|
|
46
|
+
color: var(--color-primary);
|
|
47
|
+
margin-bottom: var(--spacing-md);
|
|
48
|
+
scroll-margin-top: calc(var(--header-height) + var(--spacing-lg));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
h1 {
|
|
52
|
+
font-size: clamp(2.5rem, 5vw, 4rem);
|
|
53
|
+
font-weight: 800;
|
|
54
|
+
letter-spacing: -0.02em;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
h2 {
|
|
58
|
+
font-size: clamp(2rem, 4vw, 2.5rem);
|
|
59
|
+
font-weight: 700;
|
|
60
|
+
letter-spacing: -0.01em;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
h3 {
|
|
64
|
+
font-size: clamp(1.5rem, 3vw, 1.875rem);
|
|
65
|
+
font-weight: 600;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
h4 {
|
|
69
|
+
font-size: 1.25rem;
|
|
70
|
+
font-weight: 600;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
p {
|
|
74
|
+
margin-bottom: var(--spacing-md);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Lists */
|
|
78
|
+
ul {
|
|
79
|
+
margin-bottom: var(--spacing-md);
|
|
80
|
+
padding-left: 0;
|
|
81
|
+
list-style: none !important;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
ol {
|
|
85
|
+
margin-bottom: var(--spacing-md);
|
|
86
|
+
padding-left: 0;
|
|
87
|
+
list-style: none !important;
|
|
88
|
+
counter-reset: item;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
li {
|
|
92
|
+
margin-bottom: var(--spacing-xs);
|
|
93
|
+
position: relative;
|
|
94
|
+
padding-left: 1.5rem;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
ul > li::before {
|
|
98
|
+
content: '•' !important;
|
|
99
|
+
position: absolute !important;
|
|
100
|
+
left: 0 !important;
|
|
101
|
+
color: var(--color-secondary) !important;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
ol > li::before {
|
|
105
|
+
content: counter(item) '.' !important;
|
|
106
|
+
counter-increment: item !important;
|
|
107
|
+
position: absolute !important;
|
|
108
|
+
left: 0 !important;
|
|
109
|
+
color: var(--color-secondary) !important;
|
|
110
|
+
font-weight: 600 !important;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* Ensure ol li doesn't get ul's bullet */
|
|
114
|
+
ol li::before {
|
|
115
|
+
content: counter(item) '.' !important;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Task lists (GitHub-style checkboxes): the global `ul > li::before` bullet
|
|
119
|
+
and `li` indent don't apply here — the checkbox is the marker. Suppress the
|
|
120
|
+
blue dot and vertically center the box with its label line via flex. */
|
|
121
|
+
.task-list-item {
|
|
122
|
+
display: flex;
|
|
123
|
+
align-items: center;
|
|
124
|
+
padding-left: 0;
|
|
125
|
+
list-style: none;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.task-list-item::before {
|
|
129
|
+
content: none !important;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* Custom checkbox: replace the native control (which is disabled/read-only for
|
|
133
|
+
markdown task lists) with a box that matches the design system. Flex-center
|
|
134
|
+
so the checkmark sits in the middle of the box on both axes. */
|
|
135
|
+
.task-list-item input[type='checkbox'] {
|
|
136
|
+
appearance: none;
|
|
137
|
+
-webkit-appearance: none;
|
|
138
|
+
display: inline-flex;
|
|
139
|
+
align-items: center;
|
|
140
|
+
justify-content: center;
|
|
141
|
+
flex-shrink: 0;
|
|
142
|
+
width: 1.05em;
|
|
143
|
+
height: 1.05em;
|
|
144
|
+
margin: 0 var(--spacing-sm) 0 0;
|
|
145
|
+
background: var(--color-bg-alt);
|
|
146
|
+
border: 2px solid var(--color-border);
|
|
147
|
+
border-radius: var(--radius-sm);
|
|
148
|
+
cursor: default;
|
|
149
|
+
transition: all var(--transition-fast);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* Checked: accent fill + a CSS-drawn checkmark */
|
|
153
|
+
.task-list-item input[type='checkbox']:checked {
|
|
154
|
+
background: var(--color-secondary);
|
|
155
|
+
border-color: var(--color-secondary);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.task-list-item input[type='checkbox']:checked::after {
|
|
159
|
+
content: '';
|
|
160
|
+
width: 0.3em;
|
|
161
|
+
height: 0.55em;
|
|
162
|
+
margin-top: -0.1em;
|
|
163
|
+
border: solid #fff;
|
|
164
|
+
border-width: 0 0.16em 0.16em 0;
|
|
165
|
+
transform: rotate(45deg);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
a {
|
|
169
|
+
color: var(--color-secondary);
|
|
170
|
+
text-decoration: none;
|
|
171
|
+
transition: color var(--transition-fast);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
a:hover {
|
|
175
|
+
color: var(--color-accent);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* ============================================
|
|
179
|
+
Layout
|
|
180
|
+
============================================ */
|
|
181
|
+
|
|
182
|
+
.container {
|
|
183
|
+
width: 100%;
|
|
184
|
+
max-width: 1280px;
|
|
185
|
+
margin-left: auto;
|
|
186
|
+
margin-right: auto;
|
|
187
|
+
padding-left: var(--spacing-lg);
|
|
188
|
+
padding-right: var(--spacing-lg);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.container-narrow {
|
|
192
|
+
width: 100%;
|
|
193
|
+
max-width: 896px;
|
|
194
|
+
margin-left: auto;
|
|
195
|
+
margin-right: auto;
|
|
196
|
+
padding-left: var(--spacing-lg);
|
|
197
|
+
padding-right: var(--spacing-lg);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.section {
|
|
201
|
+
padding-top: var(--spacing-3xl);
|
|
202
|
+
padding-bottom: var(--spacing-3xl);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.grid {
|
|
206
|
+
display: grid;
|
|
207
|
+
gap: var(--spacing-xl);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.grid-2 {
|
|
211
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.grid-3 {
|
|
215
|
+
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.grid-4 {
|
|
219
|
+
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
220
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
Markdown Tables (data tables, prominent borders)
|
|
3
|
+
============================================ */
|
|
4
|
+
|
|
5
|
+
/* Markdown pipe tables always render a <thead>; raw-HTML layout tables (e.g.
|
|
6
|
+
the media-gallery image grid) do not, so :has(thead) styles only real data
|
|
7
|
+
tables and leaves layout tables borderless. */
|
|
8
|
+
table:has(thead) {
|
|
9
|
+
width: 100%;
|
|
10
|
+
border-collapse: collapse;
|
|
11
|
+
margin: var(--spacing-lg) 0;
|
|
12
|
+
border: 2px solid var(--color-text);
|
|
13
|
+
font-size: 0.9375rem;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
table:has(thead) th,
|
|
17
|
+
table:has(thead) td {
|
|
18
|
+
border: 1px solid var(--color-text);
|
|
19
|
+
padding: var(--spacing-sm) var(--spacing-md);
|
|
20
|
+
text-align: left;
|
|
21
|
+
vertical-align: top;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
table:has(thead) thead th {
|
|
25
|
+
background: var(--color-bg);
|
|
26
|
+
font-family: var(--font-display);
|
|
27
|
+
font-weight: 700;
|
|
28
|
+
color: var(--color-primary);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
table:has(thead) tbody tr:nth-child(even) {
|
|
32
|
+
background: #f8fafc;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* ============================================
|
|
36
|
+
KaTeX Math (rehype-katex output)
|
|
37
|
+
============================================ */
|
|
38
|
+
|
|
39
|
+
/* Display math ($$...$$) can be wider than the column; allow it to scroll
|
|
40
|
+
horizontally instead of overflowing the layout. */
|
|
41
|
+
.katex-display {
|
|
42
|
+
overflow-x: auto;
|
|
43
|
+
overflow-y: hidden;
|
|
44
|
+
padding: var(--spacing-sm) 0;
|
|
45
|
+
margin: var(--spacing-md) 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Keep inline math from inflating line-height */
|
|
49
|
+
.katex {
|
|
50
|
+
font-size: 1.05em;
|
|
51
|
+
line-height: normal;
|
|
52
|
+
}
|