@parche/core 0.3.0-alpha.0
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 +21 -0
- package/README.md +11 -0
- package/package.json +55 -0
- package/src/components/DynamicRenderer.astro +96 -0
- package/src/components/LayoutRenderer.astro +65 -0
- package/src/components/SectionWrapper.astro +46 -0
- package/src/components/common/LocaleSwitcher.astro +168 -0
- package/src/components/common/OptimizedImage.astro +41 -0
- package/src/components/common/ThemePanel.astro +152 -0
- package/src/components/common/ThemeSelector.astro +67 -0
- package/src/components/common/ThemeToggle.astro +82 -0
- package/src/config/font-variables.ts +29 -0
- package/src/config/fonts.ts +18 -0
- package/src/content/index.ts +11 -0
- package/src/content/schemas.ts +253 -0
- package/src/integration/index.ts +156 -0
- package/src/integration/registry.ts +193 -0
- package/src/integration/types.ts +193 -0
- package/src/integration/virtual.d.ts +326 -0
- package/src/integration/vite-plugin-parche.ts +432 -0
- package/src/layouts/BaseLayout.astro +121 -0
- package/src/routes/404.astro +46 -0
- package/src/routes/[...slug].astro +216 -0
- package/src/routes/middleware.ts +18 -0
- package/src/styles/base.css +221 -0
- package/src/styles/semantic.css +95 -0
- package/src/styles/shadcn-compat.css +120 -0
- package/src/styles/tokens.css +237 -0
- package/src/types/config.ts +102 -0
- package/src/utils/i18n.ts +79 -0
- package/src/utils/layout.ts +46 -0
- package/src/utils/metadata.ts +277 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
---
|
|
2
|
+
import 'parche:config/styles';
|
|
3
|
+
import BaseLayout from 'parche:layouts/BaseLayout';
|
|
4
|
+
import ThemePanel from 'parche:components/ThemePanel';
|
|
5
|
+
import LayoutRenderer from 'parche:LayoutRenderer';
|
|
6
|
+
import config from 'parche:config';
|
|
7
|
+
import { templateMap } from 'parche:registry/templates';
|
|
8
|
+
import { widgetMap } from 'parche:registry/widgets';
|
|
9
|
+
import { defaultLocale } from 'parche:config/i18n';
|
|
10
|
+
import { showPanel } from 'parche:config/themes';
|
|
11
|
+
import { resolveContent, getResolverPaths } from 'parche:registry/resolvers';
|
|
12
|
+
import { getEntry, render } from 'astro:content';
|
|
13
|
+
import {
|
|
14
|
+
buildSlugMap,
|
|
15
|
+
getAlternateUrls,
|
|
16
|
+
resolvePageFromSlug,
|
|
17
|
+
} from 'parche:utils/i18n';
|
|
18
|
+
import { resolveMetadata } from 'parche:utils/metadata';
|
|
19
|
+
import { resolveLayout } from 'parche:utils/layout';
|
|
20
|
+
|
|
21
|
+
export async function getStaticPaths() {
|
|
22
|
+
const slugMap = await buildSlugMap();
|
|
23
|
+
|
|
24
|
+
const pagePaths = slugMap.map((entry) => {
|
|
25
|
+
const { locale, pageKey, slug, data, entryId } = entry;
|
|
26
|
+
|
|
27
|
+
let urlSlug: string | undefined;
|
|
28
|
+
if (pageKey === 'home') {
|
|
29
|
+
urlSlug = locale === defaultLocale ? undefined : locale;
|
|
30
|
+
} else if (locale === defaultLocale) {
|
|
31
|
+
urlSlug = slug;
|
|
32
|
+
} else {
|
|
33
|
+
urlSlug = `${locale}/${slug}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
params: { slug: urlSlug },
|
|
38
|
+
props: { pageData: data, locale, pageKey, entryId },
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Add paths from app resolvers (e.g. blog posts at root level)
|
|
43
|
+
const { locales } = await import('parche:config/i18n');
|
|
44
|
+
const isDev = import.meta.env.DEV;
|
|
45
|
+
const resolverPaths = await getResolverPaths(locales, defaultLocale, { showDrafts: isDev });
|
|
46
|
+
|
|
47
|
+
return [...pagePaths, ...resolverPaths];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ---- Render-time ----
|
|
51
|
+
// Determine what we're rendering: app content (via resolver) or page content.
|
|
52
|
+
let mode: 'resolver' | 'page' = 'page';
|
|
53
|
+
let resolvedApp: Awaited<ReturnType<typeof resolveContent>> = null;
|
|
54
|
+
|
|
55
|
+
// Page-mode variables
|
|
56
|
+
let pageData: any;
|
|
57
|
+
let locale: string = Astro.currentLocale ?? defaultLocale;
|
|
58
|
+
let pageKey: string = '';
|
|
59
|
+
let entryId: string = '';
|
|
60
|
+
let alternates: { locale: string; href: string }[] = [];
|
|
61
|
+
|
|
62
|
+
const isDev = import.meta.env.DEV;
|
|
63
|
+
|
|
64
|
+
if (Astro.props.fromResolver) {
|
|
65
|
+
// Static mode — this path came from a resolver's getPaths()
|
|
66
|
+
resolvedApp = await resolveContent(Astro.params.slug!, locale, {
|
|
67
|
+
showDrafts: isDev,
|
|
68
|
+
siteUrl: config.site.url || Astro.site?.href || '',
|
|
69
|
+
siteName: config.site.name,
|
|
70
|
+
});
|
|
71
|
+
if (resolvedApp) {
|
|
72
|
+
mode = 'resolver';
|
|
73
|
+
} else {
|
|
74
|
+
return Astro.redirect('/404');
|
|
75
|
+
}
|
|
76
|
+
} else if (Astro.props.pageData) {
|
|
77
|
+
// Static mode — page data provided by getStaticPaths
|
|
78
|
+
({ pageData, locale, pageKey, entryId } = Astro.props);
|
|
79
|
+
} else {
|
|
80
|
+
// SSR mode — try resolvers first, then pages
|
|
81
|
+
resolvedApp = await resolveContent(Astro.params.slug ?? '', locale, {
|
|
82
|
+
showDrafts: isDev,
|
|
83
|
+
siteUrl: config.site.url || Astro.site?.href || '',
|
|
84
|
+
siteName: config.site.name,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (resolvedApp) {
|
|
88
|
+
mode = 'resolver';
|
|
89
|
+
} else {
|
|
90
|
+
const resolved = await resolvePageFromSlug(Astro.params.slug, defaultLocale);
|
|
91
|
+
if (!resolved) {
|
|
92
|
+
return Astro.redirect('/404');
|
|
93
|
+
}
|
|
94
|
+
({ pageData, locale, pageKey } = resolved);
|
|
95
|
+
entryId = resolved.entryId;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ---- Resolver mode: app-provided content (e.g. blog post) ----
|
|
100
|
+
let AppTemplate: any = null;
|
|
101
|
+
let AppContent: any = null;
|
|
102
|
+
let appMetadata: any = null;
|
|
103
|
+
let appExtras: any = {};
|
|
104
|
+
const RelatedPosts = widgetMap['blog/RelatedPosts'];
|
|
105
|
+
const SeriesNav = widgetMap['blog/SeriesNav'];
|
|
106
|
+
|
|
107
|
+
if (mode === 'resolver' && resolvedApp) {
|
|
108
|
+
AppTemplate = templateMap[resolvedApp.template];
|
|
109
|
+
appMetadata = resolvedApp.metadata;
|
|
110
|
+
appExtras = resolvedApp.extras;
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
const entry = await getEntry(resolvedApp.collection as any, resolvedApp.entryId as any);
|
|
114
|
+
if (entry) {
|
|
115
|
+
const { Content } = await render(entry);
|
|
116
|
+
AppContent = Content;
|
|
117
|
+
}
|
|
118
|
+
} catch { /* non-renderable entry */ }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ---- Page mode: standard page content ----
|
|
122
|
+
let pageMetadata: any = null;
|
|
123
|
+
let pageTemplate: string = 'dynamic';
|
|
124
|
+
let PageTemplateComponent: any = null;
|
|
125
|
+
let PageMarkdownContent: any = null;
|
|
126
|
+
let pageLayoutName: string = 'default';
|
|
127
|
+
|
|
128
|
+
if (mode === 'page' && pageData) {
|
|
129
|
+
const slugMap = await buildSlugMap();
|
|
130
|
+
alternates = getAlternateUrls(pageKey, slugMap, defaultLocale, Astro.site);
|
|
131
|
+
pageTemplate = pageData.template || 'dynamic';
|
|
132
|
+
|
|
133
|
+
const resolved = resolveMetadata(pageData, config, { locale });
|
|
134
|
+
resolved.title = pageKey === 'home'
|
|
135
|
+
? config.site.name
|
|
136
|
+
: `${resolved.title} — ${config.site.name}`;
|
|
137
|
+
pageMetadata = resolved;
|
|
138
|
+
|
|
139
|
+
PageTemplateComponent = pageTemplate !== 'dynamic' ? templateMap[pageTemplate] : null;
|
|
140
|
+
|
|
141
|
+
const pageEntry = await getEntry('pages', entryId);
|
|
142
|
+
if (pageEntry) {
|
|
143
|
+
try {
|
|
144
|
+
const { Content } = await render(pageEntry);
|
|
145
|
+
PageMarkdownContent = Content;
|
|
146
|
+
} catch {
|
|
147
|
+
// JSON entries don't support render()
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
pageLayoutName = pageData.layout || 'default';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const metadata = mode === 'resolver' ? appMetadata : pageMetadata;
|
|
154
|
+
const layoutName = mode === 'resolver'
|
|
155
|
+
? (resolvedApp?.layout || 'default')
|
|
156
|
+
: pageLayoutName;
|
|
157
|
+
const layoutSections = await resolveLayout(layoutName, locale, defaultLocale);
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
<BaseLayout metadata={metadata} config={config} lang={locale}>
|
|
161
|
+
{mode === 'page' && alternates.length > 0 && (
|
|
162
|
+
<Fragment slot="head">
|
|
163
|
+
{alternates.map((alt) => (
|
|
164
|
+
<link rel="alternate" hreflang={alt.locale} href={alt.href} />
|
|
165
|
+
))}
|
|
166
|
+
<link
|
|
167
|
+
rel="alternate"
|
|
168
|
+
hreflang="x-default"
|
|
169
|
+
href={alternates.find((a) => a.locale === defaultLocale)?.href}
|
|
170
|
+
/>
|
|
171
|
+
</Fragment>
|
|
172
|
+
)}
|
|
173
|
+
|
|
174
|
+
{mode === 'resolver' && resolvedApp ? (
|
|
175
|
+
<LayoutRenderer
|
|
176
|
+
layoutSections={layoutSections}
|
|
177
|
+
pageTemplate="dynamic"
|
|
178
|
+
>
|
|
179
|
+
{AppTemplate ? (
|
|
180
|
+
<AppTemplate {...resolvedApp.templateProps}>
|
|
181
|
+
{AppContent && <AppContent />}
|
|
182
|
+
</AppTemplate>
|
|
183
|
+
) : (
|
|
184
|
+
<article class="prose max-w-4xl mx-auto py-16 px-4">
|
|
185
|
+
{AppContent && <AppContent />}
|
|
186
|
+
</article>
|
|
187
|
+
)}
|
|
188
|
+
|
|
189
|
+
{appExtras.seriesNav && SeriesNav && (
|
|
190
|
+
<div class="max-w-4xl mx-auto px-4 mb-8">
|
|
191
|
+
<SeriesNav
|
|
192
|
+
seriesName={appExtras.seriesNav.seriesName}
|
|
193
|
+
posts={appExtras.seriesNav.posts}
|
|
194
|
+
currentOrder={appExtras.seriesNav.currentOrder}
|
|
195
|
+
/>
|
|
196
|
+
</div>
|
|
197
|
+
)}
|
|
198
|
+
|
|
199
|
+
{appExtras.relatedPosts && RelatedPosts && (
|
|
200
|
+
<RelatedPosts posts={appExtras.relatedPosts} />
|
|
201
|
+
)}
|
|
202
|
+
</LayoutRenderer>
|
|
203
|
+
) : (
|
|
204
|
+
<LayoutRenderer
|
|
205
|
+
layoutSections={layoutSections}
|
|
206
|
+
pageSections={pageData?.sections}
|
|
207
|
+
pageTemplate={pageTemplate}
|
|
208
|
+
PageTemplateComponent={PageTemplateComponent}
|
|
209
|
+
pageData={pageData}
|
|
210
|
+
PageMarkdownContent={PageMarkdownContent}
|
|
211
|
+
/>
|
|
212
|
+
|
|
213
|
+
)}
|
|
214
|
+
|
|
215
|
+
{showPanel && <ThemePanel />}
|
|
216
|
+
</BaseLayout>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineMiddleware } from 'astro:middleware';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parche middleware — required by Astro's `routing: 'manual'` i18n mode.
|
|
5
|
+
* Enriches Astro.locals.parche with resolved locale info.
|
|
6
|
+
* Users can override this via `routes.middleware` in parche config.
|
|
7
|
+
*/
|
|
8
|
+
export const onRequest = defineMiddleware((context, next) => {
|
|
9
|
+
// Astro's i18n system sets currentLocale automatically even in manual mode
|
|
10
|
+
// We expose it in locals for convenience in user components
|
|
11
|
+
const locale = context.currentLocale || 'en';
|
|
12
|
+
|
|
13
|
+
context.locals.parche = {
|
|
14
|
+
locale,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
return next();
|
|
18
|
+
});
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/* =================================================================
|
|
2
|
+
Capa 3: Base — Entry point del design system
|
|
3
|
+
Importa Tailwind + tokens + semánticos.
|
|
4
|
+
Define dark variant, reset mínimo, y clases tipográficas compuestas.
|
|
5
|
+
================================================================= */
|
|
6
|
+
|
|
7
|
+
@import "tailwindcss";
|
|
8
|
+
@import "./tokens.css";
|
|
9
|
+
@import "./semantic.css";
|
|
10
|
+
@import "./shadcn-compat.css";
|
|
11
|
+
|
|
12
|
+
/* Scan core's own component files. Self-relative, so it works both in the
|
|
13
|
+
monorepo and when installed. A consuming app's own files are picked up by its
|
|
14
|
+
Tailwind setup automatically. */
|
|
15
|
+
@source "../../**/*.{astro,ts}";
|
|
16
|
+
|
|
17
|
+
/* NOTE: the imported parches' component globs are appended here at build time
|
|
18
|
+
by vite-plugin-parche's transform hook — as absolute `@source` lines — since
|
|
19
|
+
relative paths can't reach sibling packages once installed from npm. */
|
|
20
|
+
|
|
21
|
+
/* Dark mode via clase .dark en <html> */
|
|
22
|
+
@custom-variant dark (&:where(.dark, .dark *));
|
|
23
|
+
|
|
24
|
+
/* ---------------------------------------------------------------
|
|
25
|
+
Reset base mínimo
|
|
26
|
+
--------------------------------------------------------------- */
|
|
27
|
+
@layer base {
|
|
28
|
+
html {
|
|
29
|
+
-webkit-font-smoothing: antialiased;
|
|
30
|
+
-moz-osx-font-smoothing: grayscale;
|
|
31
|
+
text-rendering: optimizeLegibility;
|
|
32
|
+
scroll-behavior: smooth;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
body {
|
|
36
|
+
font-family: var(--font-sans);
|
|
37
|
+
background-color: var(--color-background);
|
|
38
|
+
color: var(--color-text);
|
|
39
|
+
transition: background-color 0.2s ease, color 0.2s ease;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* ---------------------------------------------------------------
|
|
44
|
+
Clases tipográficas compuestas
|
|
45
|
+
Uso: <h1 class="type-h1">
|
|
46
|
+
--------------------------------------------------------------- */
|
|
47
|
+
@layer components {
|
|
48
|
+
.type-h1 {
|
|
49
|
+
font-family: var(--ds-font-heading);
|
|
50
|
+
font-size: var(--ds-size-h1);
|
|
51
|
+
font-weight: var(--ds-weight-h1);
|
|
52
|
+
letter-spacing: var(--ds-tracking-h1);
|
|
53
|
+
line-height: var(--ds-leading-h1);
|
|
54
|
+
color: var(--color-heading);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.type-h2 {
|
|
58
|
+
font-family: var(--ds-font-heading);
|
|
59
|
+
font-size: var(--ds-size-h2);
|
|
60
|
+
font-weight: var(--ds-weight-h2);
|
|
61
|
+
letter-spacing: var(--ds-tracking-h2);
|
|
62
|
+
line-height: var(--ds-leading-h2);
|
|
63
|
+
color: var(--color-heading);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.type-h3 {
|
|
67
|
+
font-family: var(--ds-font-heading);
|
|
68
|
+
font-size: var(--ds-size-h3);
|
|
69
|
+
font-weight: var(--ds-weight-h3);
|
|
70
|
+
letter-spacing: var(--ds-tracking-h3);
|
|
71
|
+
line-height: var(--ds-leading-h3);
|
|
72
|
+
color: var(--color-heading);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.type-body {
|
|
76
|
+
font-family: var(--ds-font-body);
|
|
77
|
+
font-size: var(--ds-size-body);
|
|
78
|
+
font-weight: var(--ds-weight-body);
|
|
79
|
+
letter-spacing: var(--ds-tracking-body);
|
|
80
|
+
line-height: var(--ds-leading-body);
|
|
81
|
+
color: var(--color-text);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.type-caption {
|
|
85
|
+
font-size: var(--ds-size-caption);
|
|
86
|
+
font-weight: var(--ds-weight-caption);
|
|
87
|
+
letter-spacing: var(--ds-tracking-caption);
|
|
88
|
+
line-height: var(--ds-leading-caption);
|
|
89
|
+
color: var(--color-muted);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.type-label {
|
|
93
|
+
font-size: var(--ds-size-label);
|
|
94
|
+
font-weight: var(--ds-weight-label);
|
|
95
|
+
letter-spacing: var(--ds-tracking-label);
|
|
96
|
+
line-height: var(--ds-leading-label);
|
|
97
|
+
text-transform: uppercase;
|
|
98
|
+
color: var(--color-muted);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* Highlight — vibrant emphasis color, distinct from shadcn's --accent (subtle bg). */
|
|
102
|
+
.text-highlight { color: var(--ds-color-highlight); }
|
|
103
|
+
.bg-highlight { background-color: var(--ds-color-highlight); }
|
|
104
|
+
|
|
105
|
+
/* ---------------------------------------------------------------
|
|
106
|
+
Prose — Markdown content styling
|
|
107
|
+
Used by the content template when rendering .md pages.
|
|
108
|
+
Mirrors the design system tokens so markdown output matches
|
|
109
|
+
the manually-styled HTML it replaces.
|
|
110
|
+
--------------------------------------------------------------- */
|
|
111
|
+
.prose {
|
|
112
|
+
font-family: var(--ds-font-body);
|
|
113
|
+
font-size: var(--ds-size-body);
|
|
114
|
+
font-weight: var(--ds-weight-body);
|
|
115
|
+
letter-spacing: var(--ds-tracking-body);
|
|
116
|
+
line-height: var(--ds-leading-body);
|
|
117
|
+
color: var(--color-muted);
|
|
118
|
+
|
|
119
|
+
& > * + * {
|
|
120
|
+
margin-top: 1.5em;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
& h2 {
|
|
124
|
+
font-family: var(--ds-font-heading);
|
|
125
|
+
font-size: var(--ds-size-h3);
|
|
126
|
+
font-weight: var(--ds-weight-h3);
|
|
127
|
+
letter-spacing: var(--ds-tracking-h3);
|
|
128
|
+
line-height: var(--ds-leading-h3);
|
|
129
|
+
color: var(--color-heading);
|
|
130
|
+
margin-top: 2em;
|
|
131
|
+
margin-bottom: 0.75em;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
& h3 {
|
|
135
|
+
font-family: var(--ds-font-heading);
|
|
136
|
+
font-size: 1.25rem;
|
|
137
|
+
font-weight: var(--ds-weight-h3);
|
|
138
|
+
letter-spacing: var(--ds-tracking-h3);
|
|
139
|
+
line-height: var(--ds-leading-h3);
|
|
140
|
+
color: var(--color-heading);
|
|
141
|
+
margin-top: 1.75em;
|
|
142
|
+
margin-bottom: 0.5em;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
& h4 {
|
|
146
|
+
font-family: var(--ds-font-heading);
|
|
147
|
+
font-size: 1.125rem;
|
|
148
|
+
font-weight: var(--ds-weight-h3);
|
|
149
|
+
color: var(--color-heading);
|
|
150
|
+
margin-top: 1.5em;
|
|
151
|
+
margin-bottom: 0.5em;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
& p {
|
|
155
|
+
margin-top: 0;
|
|
156
|
+
margin-bottom: 0;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
& a {
|
|
160
|
+
color: var(--color-primary);
|
|
161
|
+
text-decoration: none;
|
|
162
|
+
&:hover {
|
|
163
|
+
text-decoration: underline;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
& strong {
|
|
168
|
+
color: var(--color-heading);
|
|
169
|
+
font-weight: 600;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
& ul, & ol {
|
|
173
|
+
padding-left: 1.5em;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
& ul {
|
|
177
|
+
list-style-type: disc;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
& ol {
|
|
181
|
+
list-style-type: decimal;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
& li + li {
|
|
185
|
+
margin-top: 0.5em;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
& blockquote {
|
|
189
|
+
border-left: 3px solid var(--color-border);
|
|
190
|
+
padding-left: 1em;
|
|
191
|
+
color: var(--color-muted);
|
|
192
|
+
font-style: italic;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
& code {
|
|
196
|
+
font-family: var(--font-mono);
|
|
197
|
+
font-size: 0.875em;
|
|
198
|
+
background-color: var(--color-surface);
|
|
199
|
+
padding: 0.125em 0.375em;
|
|
200
|
+
border-radius: 0.25rem;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
& pre {
|
|
204
|
+
background-color: var(--color-surface);
|
|
205
|
+
padding: 1em;
|
|
206
|
+
border-radius: 0.5rem;
|
|
207
|
+
overflow-x: auto;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
& pre code {
|
|
211
|
+
background: none;
|
|
212
|
+
padding: 0;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
& hr {
|
|
216
|
+
border: none;
|
|
217
|
+
border-top: 1px solid var(--color-border);
|
|
218
|
+
margin: 2em 0;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/* =================================================================
|
|
2
|
+
Capa 2: Tokens semánticos
|
|
3
|
+
Mapea primitivos a roles contextuales. Cambia según light/dark.
|
|
4
|
+
@theme inline genera clases Tailwind que leen CSS vars,
|
|
5
|
+
permitiendo que los temas overrideen los valores.
|
|
6
|
+
================================================================= */
|
|
7
|
+
|
|
8
|
+
/* ---------------------------------------------------------------
|
|
9
|
+
Conectar variables semánticas con Tailwind
|
|
10
|
+
bg-background, text-foreground, bg-surface, etc.
|
|
11
|
+
--------------------------------------------------------------- */
|
|
12
|
+
@theme inline {
|
|
13
|
+
--color-background: var(--ds-color-background);
|
|
14
|
+
--color-foreground: var(--ds-color-foreground);
|
|
15
|
+
--color-surface: var(--ds-color-surface);
|
|
16
|
+
--color-primary: var(--ds-color-primary);
|
|
17
|
+
--color-on-primary: var(--ds-color-on-primary);
|
|
18
|
+
--color-muted: var(--ds-color-muted);
|
|
19
|
+
--color-border: var(--ds-color-border);
|
|
20
|
+
--color-heading: var(--ds-color-heading);
|
|
21
|
+
--color-text: var(--ds-color-text);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* ---------------------------------------------------------------
|
|
25
|
+
Light mode (default)
|
|
26
|
+
--------------------------------------------------------------- */
|
|
27
|
+
:root {
|
|
28
|
+
/* Color roles — AstroWind v1 defaults */
|
|
29
|
+
--ds-color-background: oklch(1 0 0);
|
|
30
|
+
--ds-color-foreground: oklch(0.07 0.01 260);
|
|
31
|
+
--ds-color-surface: oklch(1 0 0);
|
|
32
|
+
--ds-color-primary: var(--color-neutral-900) /* oklch(0.500 0.220 260) */;
|
|
33
|
+
--ds-color-on-primary: oklch(1 0 0);
|
|
34
|
+
--ds-color-muted: oklch(0.55 0.02 264.42);
|
|
35
|
+
--ds-color-border: oklch(0.90 0.006 260);
|
|
36
|
+
--ds-color-heading: oklch(0.07 0.01 260);
|
|
37
|
+
--ds-color-text: oklch(0.25 0.008 260);
|
|
38
|
+
--ds-color-highlight: oklch(0.420 0.270 295); /* v1 accent purple: rgb(109 40 217) */
|
|
39
|
+
|
|
40
|
+
/* Typography — Inter everywhere, matching v1 sizes */
|
|
41
|
+
--ds-font-heading: var(--font-sans);
|
|
42
|
+
--ds-font-body: var(--font-sans);
|
|
43
|
+
|
|
44
|
+
/* Typography — H1 */
|
|
45
|
+
--ds-size-h1: 3.75rem; /* 60px — matches v1 */
|
|
46
|
+
--ds-weight-h1: 700;
|
|
47
|
+
--ds-tracking-h1: -1.26125px;
|
|
48
|
+
--ds-leading-h1: 1.08;
|
|
49
|
+
|
|
50
|
+
/* Typography — H2 */
|
|
51
|
+
--ds-size-h2: 2.40rem;
|
|
52
|
+
--ds-weight-h2: 700;
|
|
53
|
+
--ds-tracking-h2: -1.26125px;
|
|
54
|
+
--ds-leading-h2: 1.15;
|
|
55
|
+
|
|
56
|
+
/* Typography — H3 */
|
|
57
|
+
--ds-size-h3: 1.5rem;
|
|
58
|
+
--ds-weight-h3: 600;
|
|
59
|
+
--ds-tracking-h3: 0;
|
|
60
|
+
--ds-leading-h3: 1.3;
|
|
61
|
+
|
|
62
|
+
/* Typography — Body */
|
|
63
|
+
--ds-size-body: 1rem;
|
|
64
|
+
--ds-weight-body: 400;
|
|
65
|
+
--ds-tracking-body: 0em;
|
|
66
|
+
--ds-leading-body: 1.66;
|
|
67
|
+
|
|
68
|
+
/* Typography — Caption */
|
|
69
|
+
--ds-size-caption: 0.875rem;
|
|
70
|
+
--ds-weight-caption: 400;
|
|
71
|
+
--ds-tracking-caption: 0.01em;
|
|
72
|
+
--ds-leading-caption: 1.5;
|
|
73
|
+
|
|
74
|
+
/* Typography — Label */
|
|
75
|
+
--ds-size-label: 0.875rem;
|
|
76
|
+
--ds-weight-label: 600;
|
|
77
|
+
--ds-tracking-label: 0.05em;
|
|
78
|
+
--ds-leading-label: 1.4;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* ---------------------------------------------------------------
|
|
82
|
+
Dark mode
|
|
83
|
+
--------------------------------------------------------------- */
|
|
84
|
+
.dark {
|
|
85
|
+
--ds-color-background: oklch(0.100 0.040 260); /* v1 dark navy: rgb(3 6 32) */
|
|
86
|
+
--ds-color-foreground: oklch(0.960 0.005 260);
|
|
87
|
+
--ds-color-surface: oklch(0.140 0.040 260);
|
|
88
|
+
--ds-color-primary: oklch(0.500 0.220 260); /* same blue in dark */
|
|
89
|
+
--ds-color-on-primary: oklch(1 0 0);
|
|
90
|
+
--ds-color-muted: oklch(0.55 0.015 260);
|
|
91
|
+
--ds-color-border: oklch(0.200 0.030 260);
|
|
92
|
+
--ds-color-heading: oklch(0.970 0.003 260);
|
|
93
|
+
--ds-color-text: oklch(0.800 0.010 250);
|
|
94
|
+
--ds-color-highlight: oklch(0.550 0.250 295); /* lighter purple for dark mode */
|
|
95
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/* =================================================================
|
|
2
|
+
shadcn/ui Compatibility Layer
|
|
3
|
+
Maps shadcn/ui CSS variable names to existing design system tokens.
|
|
4
|
+
This is a pure alias layer — no new values, just pointers.
|
|
5
|
+
================================================================= */
|
|
6
|
+
|
|
7
|
+
/* ---------------------------------------------------------------
|
|
8
|
+
Light mode aliases
|
|
9
|
+
--------------------------------------------------------------- */
|
|
10
|
+
:root {
|
|
11
|
+
--background: var(--ds-color-background);
|
|
12
|
+
--foreground: var(--ds-color-foreground);
|
|
13
|
+
|
|
14
|
+
--card: var(--ds-color-surface);
|
|
15
|
+
--card-foreground: var(--ds-color-foreground);
|
|
16
|
+
|
|
17
|
+
--popover: var(--ds-color-surface);
|
|
18
|
+
--popover-foreground: var(--ds-color-foreground);
|
|
19
|
+
|
|
20
|
+
--primary: var(--ds-color-primary);
|
|
21
|
+
--primary-foreground: var(--ds-color-on-primary);
|
|
22
|
+
|
|
23
|
+
--secondary: var(--color-neutral-100);
|
|
24
|
+
--secondary-foreground: var(--color-neutral-900);
|
|
25
|
+
|
|
26
|
+
--muted: var(--color-neutral-100);
|
|
27
|
+
--muted-foreground: var(--ds-color-muted);
|
|
28
|
+
|
|
29
|
+
--accent: var(--color-neutral-100);
|
|
30
|
+
--accent-foreground: var(--color-neutral-900);
|
|
31
|
+
|
|
32
|
+
--destructive: var(--color-danger-600);
|
|
33
|
+
--destructive-foreground: oklch(1 0 0);
|
|
34
|
+
|
|
35
|
+
--border: var(--ds-color-border);
|
|
36
|
+
--input: var(--ds-color-border);
|
|
37
|
+
--ring: var(--ds-color-primary);
|
|
38
|
+
|
|
39
|
+
--radius: var(--radius-md);
|
|
40
|
+
|
|
41
|
+
--chart-1: var(--color-primary-500);
|
|
42
|
+
--chart-2: var(--color-secondary-500);
|
|
43
|
+
--chart-3: var(--color-accent-500);
|
|
44
|
+
--chart-4: var(--color-success-500);
|
|
45
|
+
--chart-5: var(--color-warning-500);
|
|
46
|
+
|
|
47
|
+
/* Sidebar (shadcn/ui sidebar component) */
|
|
48
|
+
--sidebar: var(--ds-color-surface);
|
|
49
|
+
--sidebar-foreground: var(--ds-color-foreground);
|
|
50
|
+
--sidebar-primary: var(--ds-color-primary);
|
|
51
|
+
--sidebar-primary-foreground: var(--ds-color-on-primary);
|
|
52
|
+
--sidebar-accent: var(--color-neutral-100);
|
|
53
|
+
--sidebar-accent-foreground: var(--color-neutral-900);
|
|
54
|
+
--sidebar-border: var(--ds-color-border);
|
|
55
|
+
--sidebar-ring: var(--ds-color-primary);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* ---------------------------------------------------------------
|
|
59
|
+
Dark mode aliases
|
|
60
|
+
--------------------------------------------------------------- */
|
|
61
|
+
.dark {
|
|
62
|
+
--secondary: var(--color-neutral-800);
|
|
63
|
+
--secondary-foreground: var(--color-neutral-100);
|
|
64
|
+
|
|
65
|
+
--muted: var(--color-neutral-800);
|
|
66
|
+
--muted-foreground: var(--ds-color-muted);
|
|
67
|
+
|
|
68
|
+
--accent: var(--color-neutral-800);
|
|
69
|
+
--accent-foreground: var(--color-neutral-50);
|
|
70
|
+
|
|
71
|
+
--destructive: var(--color-danger-400);
|
|
72
|
+
--destructive-foreground: oklch(1 0 0);
|
|
73
|
+
|
|
74
|
+
--chart-1: var(--color-primary-400);
|
|
75
|
+
--chart-2: var(--color-secondary-400);
|
|
76
|
+
--chart-3: var(--color-accent-400);
|
|
77
|
+
--chart-4: var(--color-success-400);
|
|
78
|
+
--chart-5: var(--color-warning-400);
|
|
79
|
+
|
|
80
|
+
--sidebar-accent: var(--color-neutral-800);
|
|
81
|
+
--sidebar-accent-foreground: var(--color-neutral-50);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* ---------------------------------------------------------------
|
|
85
|
+
Bridge shadcn/ui tokens into Tailwind v4
|
|
86
|
+
Generates utilities: bg-card, text-card-foreground,
|
|
87
|
+
bg-destructive, text-muted-foreground, border-input, etc.
|
|
88
|
+
|
|
89
|
+
Note: --color-background, --color-foreground, --color-primary,
|
|
90
|
+
--color-muted, --color-border are already bridged in semantic.css
|
|
91
|
+
--------------------------------------------------------------- */
|
|
92
|
+
@theme inline {
|
|
93
|
+
--color-card: var(--card);
|
|
94
|
+
--color-card-foreground: var(--card-foreground);
|
|
95
|
+
--color-popover: var(--popover);
|
|
96
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
97
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
98
|
+
--color-secondary: var(--secondary);
|
|
99
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
100
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
101
|
+
--color-accent: var(--accent);
|
|
102
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
103
|
+
--color-destructive: var(--destructive);
|
|
104
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
105
|
+
--color-input: var(--input);
|
|
106
|
+
--color-ring: var(--ring);
|
|
107
|
+
--color-chart-1: var(--chart-1);
|
|
108
|
+
--color-chart-2: var(--chart-2);
|
|
109
|
+
--color-chart-3: var(--chart-3);
|
|
110
|
+
--color-chart-4: var(--chart-4);
|
|
111
|
+
--color-chart-5: var(--chart-5);
|
|
112
|
+
--color-sidebar: var(--sidebar);
|
|
113
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
114
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
115
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
116
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
117
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
118
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
119
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
120
|
+
}
|