@acms-subir/core 0.2.0 → 0.3.1
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/CHANGELOG.md +87 -0
- package/cli/common.mjs +35 -0
- package/cli/legacy.mjs +317 -0
- package/cli/main.mjs +66 -0
- package/cli/palette.mjs +4 -0
- package/cli/preview.mjs +50 -0
- package/cli/remote.mjs +55 -0
- package/cli/themes.mjs +127 -0
- package/cli.mjs +2 -369
- package/deploy/Caddyfile +8 -0
- package/deploy/Dockerfile +34 -0
- package/deploy/compose.https.yml +21 -0
- package/deploy/compose.yml +46 -0
- package/docs/live-video.md +91 -0
- package/docs/theme-authoring.md +103 -0
- package/email-runner.mjs +32 -0
- package/integration.mjs +189 -2
- package/ops-check.mjs +27 -0
- package/package.json +27 -5
- package/src/actions/index.ts +578 -49
- package/src/components/AiCleaner.tsx +291 -0
- package/src/components/AiSettings.tsx +575 -0
- package/src/components/CommandPalette.tsx +200 -0
- package/src/components/ContentList.astro +246 -0
- package/src/components/DesignAccess.tsx +182 -0
- package/src/components/DesignManager.tsx +418 -46
- package/src/components/EmailSettings.tsx +521 -0
- package/src/components/EventPage.astro +178 -0
- package/src/components/LegalPage.astro +27 -0
- package/src/components/LinksManager.tsx +119 -58
- package/src/components/LiveCard.astro +174 -0
- package/src/components/LiveManager.tsx +910 -0
- package/src/components/NavManager.tsx +42 -43
- package/src/components/ThemeScript.astro +50 -0
- package/src/components/ThemeSections.astro +58 -0
- package/src/components/ThemeSectionsEditor.tsx +513 -0
- package/src/components/WidgetRenderer.astro +3 -0
- package/src/components/booking/BookingFlow.astro +150 -0
- package/src/components/booking/HoursEditor.astro +63 -0
- package/src/components/builder/PageBuilder.tsx +10 -2
- package/src/components/editor/EventPanel.tsx +401 -0
- package/src/components/editor/FormatDock.tsx +81 -0
- package/src/components/editor/LivePane.tsx +122 -0
- package/src/components/editor/MobileToolbar.tsx +93 -0
- package/src/components/editor/PostEditor.tsx +605 -223
- package/src/components/editor/PreviewOverlay.tsx +242 -43
- package/src/components/editor/extensions/calendar.tsx +51 -7
- package/src/components/editor/extensions/events.tsx +153 -0
- package/src/components/editor/extensions/slash.tsx +7 -1
- package/src/components/site/Pager.astro +24 -0
- package/src/components/site/PostCard.astro +64 -0
- package/src/components/site/TagLabel.astro +11 -0
- package/src/layouts/Admin.astro +289 -49
- package/src/layouts/Site.astro +352 -59
- package/src/lib/agent-api.ts +13 -0
- package/src/lib/agent-discovery.ts +40 -0
- package/src/lib/agent.ts +114 -0
- package/src/lib/ai-config.ts +58 -0
- package/src/lib/ai-schema.ts +22 -0
- package/src/lib/ai.ts +90 -0
- package/src/lib/api-keys.ts +65 -0
- package/src/lib/api.ts +56 -0
- package/src/lib/auth.ts +25 -5
- package/src/lib/backup/content.ts +36 -0
- package/src/lib/backup/unzip.ts +12 -1
- package/src/lib/backup/zip.ts +1 -1
- package/src/lib/booking/agent.ts +108 -0
- package/src/lib/booking/availability.ts +221 -0
- package/src/lib/booking/emails.ts +115 -0
- package/src/lib/booking/forms.ts +77 -0
- package/src/lib/booking/service.ts +277 -0
- package/src/lib/booking/view.ts +54 -0
- package/src/lib/calendar/agent-schema.ts +48 -0
- package/src/lib/calendar/agent.ts +136 -0
- package/src/lib/calendar/api.ts +81 -0
- package/src/lib/calendar/cards.ts +102 -0
- package/src/lib/calendar/config.ts +52 -0
- package/src/lib/calendar/data.ts +130 -0
- package/src/lib/calendar/events.ts +261 -0
- package/src/lib/calendar/feeds.ts +43 -0
- package/src/lib/calendar/hooks.ts +38 -0
- package/src/lib/calendar/http.ts +30 -0
- package/src/lib/calendar/ics-write.ts +120 -0
- package/src/lib/calendar/ics.ts +15 -5
- package/src/lib/calendar/import.ts +203 -0
- package/src/lib/calendar/render.ts +120 -37
- package/src/lib/calendar/rrule.ts +149 -0
- package/src/lib/calendar/seo.ts +48 -0
- package/src/lib/design-service.ts +58 -0
- package/src/lib/designDiff.ts +145 -0
- package/src/lib/designPreview.ts +110 -12
- package/src/lib/documents/cleanup.ts +31 -0
- package/src/lib/documents/extract.ts +154 -0
- package/src/lib/documents/vendor.d.ts +4 -0
- package/src/lib/drafts.ts +60 -6
- package/src/lib/dynamicBlocks.ts +40 -8
- package/src/lib/email/accounts.ts +55 -0
- package/src/lib/email/config.ts +86 -0
- package/src/lib/email/migration.ts +24 -0
- package/src/lib/email/providers.ts +89 -0
- package/src/lib/email/service.ts +78 -0
- package/src/lib/email/store.ts +167 -0
- package/src/lib/email/templates.ts +18 -0
- package/src/lib/email/types.ts +32 -0
- package/src/lib/email/webhooks.ts +87 -0
- package/src/lib/email.ts +15 -0
- package/src/lib/env.ts +1 -1
- package/src/lib/legal/index.ts +25 -0
- package/src/lib/live/client.ts +88 -0
- package/src/lib/live/crypto.ts +28 -0
- package/src/lib/live/migration.ts +15 -0
- package/src/lib/live/providers.ts +152 -0
- package/src/lib/live/service.ts +170 -0
- package/src/lib/live/signals.ts +89 -0
- package/src/lib/live/store.ts +49 -0
- package/src/lib/live/types.ts +52 -0
- package/src/lib/live/view.ts +78 -0
- package/src/lib/mobile/bookings.ts +73 -0
- package/src/lib/mobile/calendar.ts +85 -0
- package/src/lib/mobile/content.ts +85 -0
- package/src/lib/mobile/http.ts +63 -0
- package/src/lib/mobile/route.ts +20 -0
- package/src/lib/ops.ts +8 -0
- package/src/lib/preview.ts +21 -1
- package/src/lib/repo/migrations.ts +221 -0
- package/src/lib/repo/sql.ts +907 -68
- package/src/lib/repo/types.ts +125 -5
- package/src/lib/scheduler.ts +27 -0
- package/src/lib/schema.ts +316 -1
- package/src/lib/search-i18n.ts +3 -0
- package/src/lib/socialIcons.ts +32 -0
- package/src/lib/tagColor.ts +11 -0
- package/src/lib/theme.ts +59 -40
- package/src/lib/themeTokens.ts +183 -0
- package/src/lib/umami.ts +108 -0
- package/src/lib/webhook-sql.ts +68 -0
- package/src/lib/webhooks.ts +147 -0
- package/src/lib/widgets.ts +1 -0
- package/src/middleware.ts +45 -3
- package/src/pages/404.astro +23 -0
- package/src/pages/[...slug].astro +14 -5
- package/src/pages/account/[action].astro +53 -0
- package/src/pages/admin/ai.astro +12 -0
- package/src/pages/admin/c/[collection]/[id].astro +4 -1
- package/src/pages/admin/c/[collection]/index.astro +127 -92
- package/src/pages/admin/calendar/bookings.astro +400 -0
- package/src/pages/admin/calendar/calendars.astro +265 -0
- package/src/pages/admin/calendar/events/[id].astro +56 -0
- package/src/pages/admin/calendar/events/index.astro +408 -0
- package/src/pages/admin/calendar/index.ts +4 -0
- package/src/pages/admin/calendar/integrations.astro +4 -0
- package/src/pages/admin/calendar/resources/[id].astro +180 -0
- package/src/pages/admin/calendar/resources/index.astro +106 -0
- package/src/pages/admin/calendar/resources/link.astro +196 -0
- package/src/pages/admin/design.astro +10 -3
- package/src/pages/admin/email.astro +13 -0
- package/src/pages/admin/index.astro +232 -85
- package/src/pages/admin/integrations.astro +237 -0
- package/src/pages/admin/links.astro +3 -3
- package/src/pages/admin/live.astro +12 -0
- package/src/pages/admin/login.astro +14 -10
- package/src/pages/admin/navigation.astro +8 -2
- package/src/pages/admin/pages/[id].astro +4 -1
- package/src/pages/admin/pages/index.astro +41 -48
- package/src/pages/admin/settings.astro +418 -367
- package/src/pages/admin/tags.astro +4 -4
- package/src/pages/admin/users.astro +38 -28
- package/src/pages/api/agent/docs.ts +33 -0
- package/src/pages/api/agent/index.ts +21 -0
- package/src/pages/api/ai/clean.ts +13 -0
- package/src/pages/api/ai/draft.ts +14 -0
- package/src/pages/api/ai/settings.ts +53 -0
- package/src/pages/api/ai/test.ts +12 -0
- package/src/pages/api/backup.ts +8 -21
- package/src/pages/api/calendar/availability.ts +35 -0
- package/src/pages/api/calendar/bookings/[id].ts +49 -0
- package/src/pages/api/calendar/bookings/index.ts +73 -0
- package/src/pages/api/calendar/calendars.ts +35 -0
- package/src/pages/api/calendar/docs.ts +66 -0
- package/src/pages/api/calendar/events/[id].ts +63 -0
- package/src/pages/api/calendar/events/index.ts +112 -0
- package/src/pages/api/calendar/resources.ts +34 -0
- package/src/pages/api/design/capabilities.ts +10 -0
- package/src/pages/api/design/index.ts +58 -0
- package/src/pages/api/design/keys.ts +24 -0
- package/src/pages/api/design/media.ts +32 -0
- package/src/pages/api/email/run.ts +11 -0
- package/src/pages/api/email/settings.ts +61 -0
- package/src/pages/api/email/webhooks/[provider].ts +4 -0
- package/src/pages/api/live/oauth/youtube/callback.ts +19 -0
- package/src/pages/api/live/settings.ts +45 -0
- package/src/pages/api/live/status.ts +7 -0
- package/src/pages/api/live/thumb.ts +48 -0
- package/src/pages/api/mcp.ts +59 -0
- package/src/pages/api/media/upload.ts +2 -2
- package/src/pages/api/ops/status.ts +39 -0
- package/src/pages/api/restore.ts +27 -37
- package/src/pages/api/search.ts +18 -4
- package/src/pages/api/v1/availability.ts +7 -0
- package/src/pages/api/v1/bookings.ts +17 -0
- package/src/pages/api/v1/calendars.ts +7 -0
- package/src/pages/api/v1/content/[id].ts +7 -0
- package/src/pages/api/v1/content/index.ts +7 -0
- package/src/pages/api/v1/events.ts +7 -0
- package/src/pages/api/v1/resources.ts +7 -0
- package/src/pages/api/webhooks/docs.ts +61 -0
- package/src/pages/author/[slug].astro +43 -35
- package/src/pages/blog/[slug].astro +137 -66
- package/src/pages/blog/index.astro +95 -30
- package/src/pages/blog/tag/[slug].astro +45 -40
- package/src/pages/book/[resource]/[link].astro +70 -0
- package/src/pages/book/[resource]/index.astro +63 -0
- package/src/pages/book/[resource].ics.ts +38 -0
- package/src/pages/book/index.astro +47 -0
- package/src/pages/book/manage/[token].astro +108 -0
- package/src/pages/calendar/[slug].ics.ts +27 -0
- package/src/pages/calendar/feed.ics.ts +15 -0
- package/src/pages/calendar/index.astro +191 -0
- package/src/pages/embed/book/[resource]/[link].astro +33 -0
- package/src/pages/embed/calendar/[slug].astro +43 -0
- package/src/pages/events/[slug].astro +74 -0
- package/src/pages/events/[slug].ics.ts +25 -0
- package/src/pages/index.astro +12 -3
- package/src/pages/legal/privacy.astro +4 -0
- package/src/pages/legal/terms.astro +4 -0
- package/src/pages/links.astro +29 -3
- package/src/pages/llms.txt.ts +4 -0
- package/src/pages/preview/[type]/[id].astro +27 -6
- package/src/pages/search.astro +40 -20
- package/src/pages/sitemap.xml.ts +16 -1
- package/src/styles/admin.css +924 -105
- package/src/styles/cards.css +92 -74
- package/src/styles/editor.css +492 -114
- package/src/styles/email-settings.css +117 -0
- package/src/styles/live-admin.css +126 -0
- package/src/styles/live.css +199 -0
- package/src/styles/sections.css +12 -0
- package/src/styles/site.css +938 -281
- package/src/styles/theme-editor.css +135 -0
- package/src/theme-sdk/contract.d.mts +619 -0
- package/src/theme-sdk/contract.mjs +81 -0
- package/src/theme-sdk/index.ts +15 -0
- package/src/theme-sdk/tokens.d.mts +148 -0
- package/src/theme-sdk/tokens.mjs +55 -0
- package/src/themes/editorial/templates/home.astro +4 -4
- package/src/themes/editorial/templates/post-minimal.astro +2 -2
- package/src/themes/editorial/theme.css +27 -37
- package/src/themes/editorial/theme.json +25 -3
- package/src/themes/field/theme.json +27 -9
- package/src/themes/ink/theme.json +30 -12
- package/src/themes/night/theme.css +4 -7
- package/src/themes/night/theme.json +28 -10
- package/src/virtual.d.ts +14 -0
- package/src/worker.ts +5 -1
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ThemeTokens } from './tokens.mjs';
|
|
3
|
+
export { ThemeTokens } from './tokens.mjs';
|
|
4
|
+
export const THEME_API_VERSION = 1;
|
|
5
|
+
export const Slug = z.string().regex(/^[a-z][a-z0-9-]{0,49}$/);
|
|
6
|
+
const text = z.string().max(20000);
|
|
7
|
+
export const Field = z.object({
|
|
8
|
+
label: z.string().min(1).max(100), type: z.enum(['text', 'textarea', 'image', 'url', 'select', 'number']),
|
|
9
|
+
default: z.union([text, z.number()]).optional(), options: z.array(z.string().max(100)).max(30).optional(),
|
|
10
|
+
}).strict();
|
|
11
|
+
export const SectionDefinition = z.object({
|
|
12
|
+
name: z.string().min(1).max(100), version: z.number().int().positive().default(1),
|
|
13
|
+
component: z.string().regex(/^sections\/[a-zA-Z0-9-]+\.astro$/).optional(),
|
|
14
|
+
variants: z.array(Slug).min(1).max(20).default(['default']),
|
|
15
|
+
fields: z.record(z.string().regex(/^[a-zA-Z][a-zA-Z0-9]*$/), Field).default({}),
|
|
16
|
+
}).strict();
|
|
17
|
+
const f = (label, type = 'text', value = '') => ({ label, type, default: value });
|
|
18
|
+
const base = { heading: f('Heading'), text: f('Supporting text', 'textarea') };
|
|
19
|
+
/** @type {Record<string, import("zod").infer<typeof SectionDefinition>>} */
|
|
20
|
+
export const BUILTIN_SECTIONS = {
|
|
21
|
+
live: { name: 'Live video', version: 1, variants: ['card','player'], fields: {} },
|
|
22
|
+
'page-content': { name: 'Existing page content', version: 1, variants: ['default'], fields: {} },
|
|
23
|
+
hero: { name: 'Hero', version: 1, variants: ['split', 'centered'], fields: { ...base, image: f('Image', 'image'), alt: f('Image description'), label: f('Button label'), href: f('Button link', 'url') } },
|
|
24
|
+
features: { name: 'Features', version: 1, variants: ['grid', 'list'], fields: { ...base, items: f('Features (one per line: title | description)', 'textarea') } },
|
|
25
|
+
testimonials: { name: 'Testimonials', version: 1, variants: ['grid', 'list'], fields: { ...base, items: f('Quotes (one per line: name | quote)', 'textarea') } },
|
|
26
|
+
faq: { name: 'FAQ', version: 1, variants: ['default'], fields: { ...base, items: f('Questions (one per line: question | answer)', 'textarea') } },
|
|
27
|
+
posts: { name: 'Posts', version: 1, variants: ['grid', 'list'], fields: { heading: f('Heading', 'text', 'Latest posts'), collection: f('Collection', 'text', 'posts'), limit: f('Number of posts', 'number', 3) } },
|
|
28
|
+
cta: { name: 'Call to action', version: 1, variants: ['default', 'panel'], fields: { ...base, label: f('Button label'), href: f('Button link', 'url') } },
|
|
29
|
+
};
|
|
30
|
+
export const SectionInstance = z.object({
|
|
31
|
+
id: z.string().min(1).max(100), type: Slug, version: z.number().int().positive().default(1),
|
|
32
|
+
variant: Slug.default('default'), hidden: z.boolean().default(false),
|
|
33
|
+
data: z.record(z.string().max(100), z.union([text, z.number().finite()])).default({}),
|
|
34
|
+
}).strict();
|
|
35
|
+
export const SectionLayout = z.object({ mode: z.enum(['content', 'sections']).default('sections'), sections: z.array(SectionInstance).max(50) }).strict()
|
|
36
|
+
.refine(v => new Set(v.sections.map(s => s.id)).size === v.sections.length, 'Section IDs must be unique');
|
|
37
|
+
export const SectionPages = z.record(z.string().min(1).max(100), SectionLayout);
|
|
38
|
+
export const PostOptions = z.object({
|
|
39
|
+
cover: z.enum(['normal', 'full', 'overlay', 'hidden']).default('normal'),
|
|
40
|
+
showDate: z.boolean().default(true),
|
|
41
|
+
showReadingTime: z.boolean().default(true),
|
|
42
|
+
byline: z.boolean().default(true),
|
|
43
|
+
authorCard: z.boolean().default(true),
|
|
44
|
+
share: z.boolean().default(true),
|
|
45
|
+
related: z.boolean().default(true),
|
|
46
|
+
// reading-progress bar across the top, and the sticky "On this page" rail
|
|
47
|
+
progress: z.boolean().default(true),
|
|
48
|
+
toc: z.boolean().default(true),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export const ThemeManifest = z.object({
|
|
52
|
+
name: z.string().min(1).max(120), description: z.string().max(2000).default(''), slug: Slug.optional(),
|
|
53
|
+
version: z.string().regex(/^\d+\.\d+\.\d+(?:-[a-zA-Z0-9.-]+)?$/).default('1.0.0'),
|
|
54
|
+
apiVersion: z.literal(1).default(1), core: z.string().default('>=0.2.0 <1.0.0'),
|
|
55
|
+
author: z.string().max(200).optional(), license: z.string().max(100).optional(),
|
|
56
|
+
tokens: ThemeTokens.default({}), post: PostOptions.default({}),
|
|
57
|
+
templates: z.array(z.string().regex(/^(home|event|post-[a-z0-9-]+|page-[a-z0-9-]+)\.astro$/)).default([]),
|
|
58
|
+
sections: z.record(Slug, SectionDefinition).default({}),
|
|
59
|
+
assets: z.array(z.string().regex(/^assets\/(?!.*\.\.)[a-zA-Z0-9_./-]+$/)).default([]),
|
|
60
|
+
}).strict();
|
|
61
|
+
/** @param {Partial<import("zod").infer<typeof ThemeManifest>>} [manifest] */
|
|
62
|
+
export function sectionDefinitions(manifest = {}) {
|
|
63
|
+
return { ...BUILTIN_SECTIONS, ...(manifest.sections ?? {}) };
|
|
64
|
+
}
|
|
65
|
+
/** @param {import("zod").infer<typeof SectionLayout>} layout
|
|
66
|
+
* @param {Record<string, import("zod").infer<typeof SectionDefinition>>} definitions */
|
|
67
|
+
export function sectionIssues(layout, definitions) {
|
|
68
|
+
return layout.sections.flatMap(s => {
|
|
69
|
+
const d = definitions[s.type];
|
|
70
|
+
if (!d) return [{ id: s.id, message: `Unavailable section: ${s.type}. Its data is preserved.` }];
|
|
71
|
+
if (s.version !== d.version) return [{ id: s.id, message: `Section ${s.type} needs a migration from v${s.version} to v${d.version}.` }];
|
|
72
|
+
if (!d.variants.includes(s.variant)) return [{ id: s.id, message: `Unsupported variant: ${s.variant}` }];
|
|
73
|
+
return Object.entries(d.fields).flatMap(([key, field]) => {
|
|
74
|
+
const value = s.data[key] ?? field.default ?? (field.type === 'number' ? 0 : '');
|
|
75
|
+
const valid = field.type === 'number' ? typeof value === 'number' && Number.isFinite(value)
|
|
76
|
+
: typeof value === 'string' && (field.type !== 'select' || field.options?.includes(value));
|
|
77
|
+
const safeLink = !['url','image'].includes(field.type) || value === '' || (typeof value === 'string' && /^(https?:\/\/|mailto:|tel:|\/(?!\/)|#)/i.test(value.trim()));
|
|
78
|
+
return valid && safeLink ? [] : [{ id: s.id, message: `Invalid ${field.label}` }];
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { ThemeTokens, ThemeManifest, PostOptions, SectionDefinition, SectionInstance, SectionLayout, SectionPages, BUILTIN_SECTIONS, sectionDefinitions, sectionIssues, THEME_API_VERSION } from './contract.mjs';
|
|
2
|
+
export { CONTENT, postsIndexUrl, postUrl, tagUrl, collectionUrl } from '../lib/content';
|
|
3
|
+
export { formatDate } from '../lib/dates';
|
|
4
|
+
export type { Page, Post, Settings, Tag, CalEvent, Calendar, User } from '../lib/schema';
|
|
5
|
+
import type { Page, Post, Settings, Tag, CalEvent, Calendar, User } from '../lib/schema';
|
|
6
|
+
export interface HomeProps { settings: Settings; page: Page | null; posts: Post[]; tags: Tag[] }
|
|
7
|
+
export interface PageProps { settings: Settings; page: Page; html: string }
|
|
8
|
+
export interface PostProps { settings: Settings; post: Post; html: string; authors: User[]; related: Post[] }
|
|
9
|
+
import type { Occurrence } from '../lib/calendar/events';
|
|
10
|
+
export interface EventProps { settings: Settings; event: CalEvent; html: string; calendars: Calendar[]; origin: string; occurrence: Occurrence; upcoming: Pick<Occurrence, 'event' | 'start' | 'end' | 'allDay' | 'days'>[]; preview?: boolean; rsvp?: {going:number;left:number|null;maxSeats:number;error:string;open:boolean}|null }
|
|
11
|
+
export interface SectionProps { data: Record<string, string | number>; variant: string; settings: Settings; page: Page }
|
|
12
|
+
|
|
13
|
+
export { slugify, authorSlug } from '../lib/schema';
|
|
14
|
+
|
|
15
|
+
export { localeFromPath, localeHomeUrl } from '../lib/i18n';
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
export const DarkTokens: z.ZodObject<{
|
|
2
|
+
bg: z.ZodOptional<z.ZodString>;
|
|
3
|
+
ink: z.ZodOptional<z.ZodString>;
|
|
4
|
+
muted: z.ZodOptional<z.ZodString>;
|
|
5
|
+
line: z.ZodOptional<z.ZodString>;
|
|
6
|
+
accent: z.ZodOptional<z.ZodString>;
|
|
7
|
+
panel: z.ZodOptional<z.ZodString>;
|
|
8
|
+
soft: z.ZodOptional<z.ZodString>;
|
|
9
|
+
}, "strip", z.ZodTypeAny, {
|
|
10
|
+
panel?: string | undefined;
|
|
11
|
+
bg?: string | undefined;
|
|
12
|
+
ink?: string | undefined;
|
|
13
|
+
muted?: string | undefined;
|
|
14
|
+
line?: string | undefined;
|
|
15
|
+
accent?: string | undefined;
|
|
16
|
+
soft?: string | undefined;
|
|
17
|
+
}, {
|
|
18
|
+
panel?: string | undefined;
|
|
19
|
+
bg?: string | undefined;
|
|
20
|
+
ink?: string | undefined;
|
|
21
|
+
muted?: string | undefined;
|
|
22
|
+
line?: string | undefined;
|
|
23
|
+
accent?: string | undefined;
|
|
24
|
+
soft?: string | undefined;
|
|
25
|
+
}>;
|
|
26
|
+
export const ThemeTokens: z.ZodObject<{
|
|
27
|
+
bg: z.ZodDefault<z.ZodString>;
|
|
28
|
+
ink: z.ZodDefault<z.ZodString>;
|
|
29
|
+
muted: z.ZodDefault<z.ZodString>;
|
|
30
|
+
line: z.ZodDefault<z.ZodString>;
|
|
31
|
+
accent: z.ZodDefault<z.ZodString>;
|
|
32
|
+
panel: z.ZodDefault<z.ZodString>;
|
|
33
|
+
soft: z.ZodDefault<z.ZodString>;
|
|
34
|
+
accentInk: z.ZodDefault<z.ZodString>;
|
|
35
|
+
button: z.ZodDefault<z.ZodEnum<["ink", "accent"]>>;
|
|
36
|
+
dark: z.ZodDefault<z.ZodObject<{
|
|
37
|
+
bg: z.ZodOptional<z.ZodString>;
|
|
38
|
+
ink: z.ZodOptional<z.ZodString>;
|
|
39
|
+
muted: z.ZodOptional<z.ZodString>;
|
|
40
|
+
line: z.ZodOptional<z.ZodString>;
|
|
41
|
+
accent: z.ZodOptional<z.ZodString>;
|
|
42
|
+
panel: z.ZodOptional<z.ZodString>;
|
|
43
|
+
soft: z.ZodOptional<z.ZodString>;
|
|
44
|
+
}, "strip", z.ZodTypeAny, {
|
|
45
|
+
panel?: string | undefined;
|
|
46
|
+
bg?: string | undefined;
|
|
47
|
+
ink?: string | undefined;
|
|
48
|
+
muted?: string | undefined;
|
|
49
|
+
line?: string | undefined;
|
|
50
|
+
accent?: string | undefined;
|
|
51
|
+
soft?: string | undefined;
|
|
52
|
+
}, {
|
|
53
|
+
panel?: string | undefined;
|
|
54
|
+
bg?: string | undefined;
|
|
55
|
+
ink?: string | undefined;
|
|
56
|
+
muted?: string | undefined;
|
|
57
|
+
line?: string | undefined;
|
|
58
|
+
accent?: string | undefined;
|
|
59
|
+
soft?: string | undefined;
|
|
60
|
+
}>>;
|
|
61
|
+
scheme: z.ZodDefault<z.ZodEnum<["light", "dark", "auto"]>>;
|
|
62
|
+
radius: z.ZodDefault<z.ZodEnum<["sharp", "soft", "round", "smooth"]>>;
|
|
63
|
+
elevation: z.ZodDefault<z.ZodEnum<["flat", "soft", "lifted"]>>;
|
|
64
|
+
density: z.ZodDefault<z.ZodEnum<["compact", "cozy", "airy"]>>;
|
|
65
|
+
headingFont: z.ZodDefault<z.ZodString>;
|
|
66
|
+
bodyFont: z.ZodDefault<z.ZodString>;
|
|
67
|
+
monoFont: z.ZodDefault<z.ZodString>;
|
|
68
|
+
googleFonts: z.ZodDefault<z.ZodString>;
|
|
69
|
+
displayWeight: z.ZodDefault<z.ZodNumber>;
|
|
70
|
+
displayTracking: z.ZodDefault<z.ZodString>;
|
|
71
|
+
baseSize: z.ZodDefault<z.ZodNumber>;
|
|
72
|
+
width: z.ZodDefault<z.ZodNumber>;
|
|
73
|
+
wideWidth: z.ZodDefault<z.ZodNumber>;
|
|
74
|
+
header: z.ZodDefault<z.ZodEnum<["floating", "bar", "minimal", "centered"]>>;
|
|
75
|
+
footer: z.ZodDefault<z.ZodEnum<["columns", "simple"]>>;
|
|
76
|
+
blogLayout: z.ZodDefault<z.ZodEnum<["auto", "cards", "list"]>>;
|
|
77
|
+
}, "strip", z.ZodTypeAny, {
|
|
78
|
+
panel: string;
|
|
79
|
+
bg: string;
|
|
80
|
+
ink: string;
|
|
81
|
+
muted: string;
|
|
82
|
+
line: string;
|
|
83
|
+
accent: string;
|
|
84
|
+
soft: string;
|
|
85
|
+
accentInk: string;
|
|
86
|
+
button: "ink" | "accent";
|
|
87
|
+
dark: {
|
|
88
|
+
panel?: string | undefined;
|
|
89
|
+
bg?: string | undefined;
|
|
90
|
+
ink?: string | undefined;
|
|
91
|
+
muted?: string | undefined;
|
|
92
|
+
line?: string | undefined;
|
|
93
|
+
accent?: string | undefined;
|
|
94
|
+
soft?: string | undefined;
|
|
95
|
+
};
|
|
96
|
+
scheme: "dark" | "light" | "auto";
|
|
97
|
+
radius: "soft" | "sharp" | "round" | "smooth";
|
|
98
|
+
elevation: "flat" | "soft" | "lifted";
|
|
99
|
+
density: "compact" | "cozy" | "airy";
|
|
100
|
+
headingFont: string;
|
|
101
|
+
bodyFont: string;
|
|
102
|
+
monoFont: string;
|
|
103
|
+
googleFonts: string;
|
|
104
|
+
displayWeight: number;
|
|
105
|
+
displayTracking: string;
|
|
106
|
+
baseSize: number;
|
|
107
|
+
width: number;
|
|
108
|
+
wideWidth: number;
|
|
109
|
+
header: "centered" | "floating" | "bar" | "minimal";
|
|
110
|
+
footer: "columns" | "simple";
|
|
111
|
+
blogLayout: "list" | "auto" | "cards";
|
|
112
|
+
}, {
|
|
113
|
+
panel?: string | undefined;
|
|
114
|
+
bg?: string | undefined;
|
|
115
|
+
ink?: string | undefined;
|
|
116
|
+
muted?: string | undefined;
|
|
117
|
+
line?: string | undefined;
|
|
118
|
+
accent?: string | undefined;
|
|
119
|
+
soft?: string | undefined;
|
|
120
|
+
accentInk?: string | undefined;
|
|
121
|
+
button?: "ink" | "accent" | undefined;
|
|
122
|
+
dark?: {
|
|
123
|
+
panel?: string | undefined;
|
|
124
|
+
bg?: string | undefined;
|
|
125
|
+
ink?: string | undefined;
|
|
126
|
+
muted?: string | undefined;
|
|
127
|
+
line?: string | undefined;
|
|
128
|
+
accent?: string | undefined;
|
|
129
|
+
soft?: string | undefined;
|
|
130
|
+
} | undefined;
|
|
131
|
+
scheme?: "dark" | "light" | "auto" | undefined;
|
|
132
|
+
radius?: "soft" | "sharp" | "round" | "smooth" | undefined;
|
|
133
|
+
elevation?: "flat" | "soft" | "lifted" | undefined;
|
|
134
|
+
density?: "compact" | "cozy" | "airy" | undefined;
|
|
135
|
+
headingFont?: string | undefined;
|
|
136
|
+
bodyFont?: string | undefined;
|
|
137
|
+
monoFont?: string | undefined;
|
|
138
|
+
googleFonts?: string | undefined;
|
|
139
|
+
displayWeight?: number | undefined;
|
|
140
|
+
displayTracking?: string | undefined;
|
|
141
|
+
baseSize?: number | undefined;
|
|
142
|
+
width?: number | undefined;
|
|
143
|
+
wideWidth?: number | undefined;
|
|
144
|
+
header?: "centered" | "floating" | "bar" | "minimal" | undefined;
|
|
145
|
+
footer?: "columns" | "simple" | undefined;
|
|
146
|
+
blogLayout?: "list" | "auto" | "cards" | undefined;
|
|
147
|
+
}>;
|
|
148
|
+
import { z } from 'zod';
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const hex = z.string();
|
|
3
|
+
|
|
4
|
+
// the dark-scheme palette; any key left out is generated from the light one
|
|
5
|
+
export const DarkTokens = z.object({
|
|
6
|
+
bg: hex.optional(),
|
|
7
|
+
ink: hex.optional(),
|
|
8
|
+
muted: hex.optional(),
|
|
9
|
+
line: hex.optional(),
|
|
10
|
+
accent: hex.optional(),
|
|
11
|
+
panel: hex.optional(),
|
|
12
|
+
soft: hex.optional(),
|
|
13
|
+
live: hex.optional(),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const ThemeTokens = z.object({
|
|
17
|
+
// color (light scheme)
|
|
18
|
+
bg: hex.default('#f6f5f2'),
|
|
19
|
+
ink: hex.default('#17181c'),
|
|
20
|
+
muted: hex.default('#62646c'),
|
|
21
|
+
line: hex.default('#e5e2db'),
|
|
22
|
+
accent: hex.default('#3355ff'),
|
|
23
|
+
panel: hex.default('#fdfcfa'),
|
|
24
|
+
soft: hex.default('#efede8'),
|
|
25
|
+
// text on an accent fill; '' = pick white or near-black by contrast
|
|
26
|
+
accentInk: hex.default(''),
|
|
27
|
+
// the live-video signal color (LIVE tag, markers); '' = a tuned red
|
|
28
|
+
live: hex.default(''),
|
|
29
|
+
// primary buttons: ink-filled (default) or accent-filled
|
|
30
|
+
button: z.enum(['ink', 'accent']).default('ink'),
|
|
31
|
+
// dark scheme
|
|
32
|
+
dark: DarkTokens.default({}),
|
|
33
|
+
scheme: z.enum(['light', 'dark', 'auto']).default('auto'),
|
|
34
|
+
// shape + depth + rhythm
|
|
35
|
+
radius: z.enum(['sharp', 'soft', 'round', 'smooth']).default('round'),
|
|
36
|
+
elevation: z.enum(['flat', 'soft', 'lifted']).default('soft'),
|
|
37
|
+
density: z.enum(['compact', 'cozy', 'airy']).default('cozy'),
|
|
38
|
+
// type
|
|
39
|
+
headingFont: z.string().default("'Geist', ui-sans-serif, system-ui, sans-serif"),
|
|
40
|
+
bodyFont: z.string().default("'Geist', ui-sans-serif, system-ui, sans-serif"),
|
|
41
|
+
monoFont: z.string().default("'Geist Mono', ui-monospace, 'SF Mono', monospace"),
|
|
42
|
+
// the `family=` query for fonts.googleapis.com/css2; empty = no webfont
|
|
43
|
+
googleFonts: z.string().default('family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500'),
|
|
44
|
+
displayWeight: z.coerce.number().default(650),
|
|
45
|
+
displayTracking: z.string().default('-0.04em'),
|
|
46
|
+
baseSize: z.coerce.number().default(17),
|
|
47
|
+
// layout
|
|
48
|
+
width: z.coerce.number().default(720),
|
|
49
|
+
wideWidth: z.coerce.number().default(1080),
|
|
50
|
+
header: z.enum(['floating', 'bar', 'minimal', 'centered']).default('floating'),
|
|
51
|
+
footer: z.enum(['columns', 'simple']).default('columns'),
|
|
52
|
+
// 'auto' = cards when at least half the listed posts have a cover, else list
|
|
53
|
+
blogLayout: z.enum(['auto', 'cards', 'list']).default('auto'),
|
|
54
|
+
});
|
|
55
|
+
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
// (and theme.css) to redesign the homepage; the words stay UI-editable because
|
|
4
4
|
// the isHome page's rendered body is injected as the hero. Sections are driven
|
|
5
5
|
// by data: `featured` posts, latest, and the `projects` / `log` tags.
|
|
6
|
-
import type { Page, Post, Settings, Tag } from '
|
|
7
|
-
import { slugify } from '
|
|
8
|
-
import { formatDate } from '
|
|
9
|
-
import { CONTENT, postsIndexUrl, postUrl, tagUrl } from '
|
|
6
|
+
import type { Page, Post, Settings, Tag } from '@acms-subir/core/theme';
|
|
7
|
+
import { slugify } from '@acms-subir/core/theme';
|
|
8
|
+
import { formatDate } from '@acms-subir/core/theme';
|
|
9
|
+
import { CONTENT, postsIndexUrl, postUrl, tagUrl } from '@acms-subir/core/theme';
|
|
10
10
|
|
|
11
11
|
interface Props {
|
|
12
12
|
settings: Settings;
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
// authors — ordered User[] (may be empty)
|
|
10
10
|
// settings — site Settings
|
|
11
11
|
// related — up to 3 related Post[]
|
|
12
|
-
import type { Post, Settings, User } from '
|
|
13
|
-
import { authorSlug } from '
|
|
12
|
+
import type { Post, Settings, User } from '@acms-subir/core/theme';
|
|
13
|
+
import { authorSlug } from '@acms-subir/core/theme';
|
|
14
14
|
|
|
15
15
|
interface Props {
|
|
16
16
|
post: Post;
|
|
@@ -4,15 +4,18 @@ body { font-size: 16.5px; }
|
|
|
4
4
|
h1, h2, h3 { font-weight: 550; letter-spacing: -0.015em; }
|
|
5
5
|
article h1 { font-size: 42px; line-height: 1.12; }
|
|
6
6
|
|
|
7
|
-
.site-header {
|
|
8
|
-
.site-header .brand { font-family: var(--font-heading); font-size: 20px; font-weight: 550; }
|
|
7
|
+
.site-header .brand { font-family: var(--font-heading); font-size: 22px; font-weight: 550; letter-spacing: -0.01em; }
|
|
9
8
|
.site-nav a { text-transform: uppercase; font-size: 12px; letter-spacing: 0.1em; }
|
|
10
|
-
.site-nav a
|
|
9
|
+
.site-nav a:hover { background: none; }
|
|
10
|
+
.site-nav a[aria-current='page'] { background: none; box-shadow: inset 0 -1.5px 0 var(--ink); border-radius: 0; font-weight: 400; }
|
|
11
11
|
|
|
12
12
|
.post-list a { font-family: var(--font-heading); font-weight: 550; }
|
|
13
13
|
.post-meta { text-transform: uppercase; font-size: 11.5px; letter-spacing: 0.08em; }
|
|
14
14
|
.post-cover { border-radius: 4px; }
|
|
15
|
-
.tag {
|
|
15
|
+
.tag { text-transform: uppercase; font-size: 11px; letter-spacing: 0.08em; }
|
|
16
|
+
.post-card-title { font-family: var(--font-heading); font-weight: 550; font-size: 22px; }
|
|
17
|
+
.post-card.is-feature .post-card-title { font-weight: 500; }
|
|
18
|
+
.filter-tabs a { text-transform: uppercase; font-size: 12px; letter-spacing: 0.08em; }
|
|
16
19
|
|
|
17
20
|
.site-footer { text-align: center; text-transform: uppercase; font-size: 12px; letter-spacing: 0.08em; }
|
|
18
21
|
|
|
@@ -63,46 +66,33 @@ article h1 { font-size: 42px; line-height: 1.12; }
|
|
|
63
66
|
.ed-feature .ed-excerpt { max-width: 58ch; }
|
|
64
67
|
|
|
65
68
|
/* ---- post page: Ghost-style editorial treatment ---- */
|
|
66
|
-
.post-header { text-align: center;
|
|
67
|
-
.post-kicker {
|
|
68
|
-
.post-header
|
|
69
|
-
.post-
|
|
70
|
-
|
|
71
|
-
.post-
|
|
72
|
-
.post-
|
|
73
|
-
.post-
|
|
74
|
-
|
|
75
|
-
/*
|
|
76
|
-
|
|
77
|
-
.post-cover { margin-top: 26px; margin-bottom: 40px; }
|
|
78
|
-
|
|
79
|
-
/* share as a floating rail beside the text on desktop */
|
|
80
|
-
@media (min-width: 1000px) {
|
|
81
|
-
.post-layout { display: grid; grid-template-columns: minmax(0, 1fr) 44px; gap: 40px; align-items: start; }
|
|
82
|
-
.post-share { flex-direction: column; align-items: center; gap: 12px; margin: 4px 0 0; position: sticky; top: 40px; }
|
|
83
|
-
.post-share-label { margin: 0 0 2px; }
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/* author card: serif italic name, centered block like the reference */
|
|
87
|
-
.author-card { gap: 22px; margin-top: 44px; padding-top: 32px; }
|
|
69
|
+
.post-header { text-align: center; }
|
|
70
|
+
.post-kicker { justify-content: center; }
|
|
71
|
+
.post-header h1 { font-weight: 500; letter-spacing: -0.02em; }
|
|
72
|
+
.post-dek { max-width: 56ch; margin-left: auto; margin-right: auto; }
|
|
73
|
+
.post-byline-row { flex-direction: column; gap: 14px; }
|
|
74
|
+
.post-byline { flex-direction: column; gap: 8px; }
|
|
75
|
+
.post-byline > div { align-items: center; }
|
|
76
|
+
.post-byline .post-meta { text-transform: uppercase; font-size: 11.5px; letter-spacing: 0.08em; }
|
|
77
|
+
|
|
78
|
+
/* author card: flat, serif italic name, like the reference */
|
|
79
|
+
.author-card { background: none; border: 0; border-top: 1px solid var(--line); border-radius: 0; padding: 32px 0 0; gap: 22px; }
|
|
88
80
|
.author-card .author-avatar { width: 76px; height: 76px; }
|
|
89
81
|
.author-card-name { font-family: var(--font-heading); font-style: italic; font-weight: 500; font-size: 30px; margin: 2px 0 6px; }
|
|
90
82
|
.author-card-bio { font-size: 15.5px; line-height: 1.6; }
|
|
91
|
-
.author-card-more { color: var(--muted); border-bottom: 1px solid var(--line); padding-bottom: 2px; }
|
|
92
|
-
.author-card-more:hover { color: var(--ink); }
|
|
93
83
|
|
|
94
84
|
/* related reading */
|
|
95
|
-
.related { margin-top: 48px; }
|
|
96
85
|
.related-title { font-style: italic; font-weight: 500; font-size: 30px; }
|
|
97
|
-
.related-card h3 { font-family: var(--font-heading); font-weight: 550; font-size: 20px; }
|
|
98
|
-
.related-thumb img { border-radius: 4px; }
|
|
99
86
|
|
|
100
|
-
/*
|
|
101
|
-
.
|
|
102
|
-
.
|
|
103
|
-
.
|
|
104
|
-
.author-hero .tag-desc { max-width: 52ch; margin: 0 auto; }
|
|
87
|
+
/* archive heads */
|
|
88
|
+
.page-head h1, .profile-head h1 { font-weight: 500; letter-spacing: -0.02em; }
|
|
89
|
+
.profile-head { flex-direction: column; text-align: center; }
|
|
90
|
+
.profile-head .page-lede { margin: 0 auto; }
|
|
105
91
|
|
|
106
92
|
/* overlay hero: keep the serif display sizing on the image variant too */
|
|
107
93
|
.post-hero-content h1 { font-size: clamp(34px, 6vw, 52px); font-weight: 500; }
|
|
108
|
-
|
|
94
|
+
|
|
95
|
+
/* live video: square-cut tag and spaced caps to match the masthead */
|
|
96
|
+
:root { --live-tag-radius: 2px; }
|
|
97
|
+
.pc-live-tag, .pc-live-kicker, .pc-live-mark { letter-spacing: 0.14em; }
|
|
98
|
+
.pc-live[data-layout='split'] .pc-live-title-text { font-weight: 500; }
|
|
@@ -1,18 +1,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "Editorial",
|
|
3
|
-
"description": "Serif editorial magazine
|
|
3
|
+
"description": "Serif editorial magazine: Fraunces display headings, warm paper, crimson accents, flat surfaces. Ships a coded homepage with Featured / Latest / tag sections.",
|
|
4
4
|
"tokens": {
|
|
5
5
|
"bg": "#f7f5f0",
|
|
6
6
|
"ink": "#181712",
|
|
7
7
|
"muted": "#6d685e",
|
|
8
8
|
"line": "#e4e0d5",
|
|
9
|
+
"live": "#b8261b",
|
|
9
10
|
"accent": "#b33a2e",
|
|
10
11
|
"panel": "#fdfcf9",
|
|
11
12
|
"soft": "#edeae1",
|
|
12
13
|
"headingFont": "'Fraunces', Georgia, serif",
|
|
13
14
|
"bodyFont": "'Inter', system-ui, sans-serif",
|
|
14
|
-
"googleFonts": "family=Fraunces:ital,opsz,wght@0,9..144,400;0,9..144,550;1,9..144,400;1,9..144,
|
|
15
|
-
"width": 720
|
|
15
|
+
"googleFonts": "family=Fraunces:ital,opsz,wght@0,9..144,400;0,9..144,500;0,9..144,550;1,9..144,400;1,9..144,500&family=Inter:wght@400;500;600&family=Geist+Mono:wght@400;500",
|
|
16
|
+
"width": 720,
|
|
17
|
+
"dark": {
|
|
18
|
+
"bg": "#16140f",
|
|
19
|
+
"ink": "#ece6da",
|
|
20
|
+
"muted": "#a39c8e",
|
|
21
|
+
"line": "#2d2a24",
|
|
22
|
+
"live": "#f26b57",
|
|
23
|
+
"accent": "#e0735f",
|
|
24
|
+
"panel": "#1d1b16",
|
|
25
|
+
"soft": "#25221c"
|
|
26
|
+
},
|
|
27
|
+
"button": "accent",
|
|
28
|
+
"radius": "soft",
|
|
29
|
+
"elevation": "flat",
|
|
30
|
+
"density": "airy",
|
|
31
|
+
"monoFont": "'Geist Mono', ui-monospace, monospace",
|
|
32
|
+
"displayWeight": 500,
|
|
33
|
+
"displayTracking": "-0.02em",
|
|
34
|
+
"baseSize": 17.5,
|
|
35
|
+
"header": "centered",
|
|
36
|
+
"footer": "simple",
|
|
37
|
+
"wideWidth": 1040
|
|
16
38
|
},
|
|
17
39
|
"post": {
|
|
18
40
|
"cover": "full"
|
|
@@ -1,17 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "Field Notes",
|
|
3
|
-
"description": "
|
|
3
|
+
"description": "Calm green-tinted paper with a humanist sans, generous spacing and smooth corners. Notebook-like.",
|
|
4
4
|
"tokens": {
|
|
5
|
-
"bg": "#
|
|
6
|
-
"ink": "#
|
|
7
|
-
"muted": "#
|
|
5
|
+
"bg": "#f2f5f0",
|
|
6
|
+
"ink": "#1f261e",
|
|
7
|
+
"muted": "#5f6a58",
|
|
8
8
|
"line": "#dde3d6",
|
|
9
|
+
"live": "#c2412d",
|
|
9
10
|
"accent": "#3d6b45",
|
|
10
|
-
"panel": "#
|
|
11
|
+
"panel": "#fbfdf9",
|
|
11
12
|
"soft": "#e6ebdf",
|
|
12
|
-
"headingFont": "'
|
|
13
|
-
"bodyFont": "'
|
|
14
|
-
"googleFonts": "family=
|
|
15
|
-
"width": 720
|
|
13
|
+
"headingFont": "'Plus Jakarta Sans', ui-sans-serif, system-ui, sans-serif",
|
|
14
|
+
"bodyFont": "'Plus Jakarta Sans', ui-sans-serif, system-ui, sans-serif",
|
|
15
|
+
"googleFonts": "family=Plus+Jakarta+Sans:wght@400;500;600;700&family=Geist+Mono:wght@400;500",
|
|
16
|
+
"width": 720,
|
|
17
|
+
"dark": {
|
|
18
|
+
"bg": "#121611",
|
|
19
|
+
"ink": "#e6ebe2",
|
|
20
|
+
"muted": "#9aa596",
|
|
21
|
+
"line": "#283026",
|
|
22
|
+
"accent": "#7cc28a",
|
|
23
|
+
"panel": "#181d17",
|
|
24
|
+
"soft": "#1f261e"
|
|
25
|
+
},
|
|
26
|
+
"button": "accent",
|
|
27
|
+
"radius": "smooth",
|
|
28
|
+
"elevation": "soft",
|
|
29
|
+
"density": "airy",
|
|
30
|
+
"displayWeight": 650,
|
|
31
|
+
"displayTracking": "-0.035em",
|
|
32
|
+
"header": "bar",
|
|
33
|
+
"footer": "columns"
|
|
16
34
|
}
|
|
17
35
|
}
|
|
@@ -1,17 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "Ink",
|
|
3
|
-
"description": "The
|
|
3
|
+
"description": "The default. Soft-modern Geist sans on a warm canvas, ink buttons, a cobalt accent, rounded media, and automatic light and dark.",
|
|
4
4
|
"tokens": {
|
|
5
|
-
"bg": "#
|
|
6
|
-
"ink": "#
|
|
7
|
-
"muted": "#
|
|
8
|
-
"line": "#
|
|
9
|
-
"accent": "#
|
|
10
|
-
"panel": "#
|
|
11
|
-
"soft": "#
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
"bg": "#f6f5f2",
|
|
6
|
+
"ink": "#17181c",
|
|
7
|
+
"muted": "#62646c",
|
|
8
|
+
"line": "#e5e2db",
|
|
9
|
+
"accent": "#3355ff",
|
|
10
|
+
"panel": "#fdfcfa",
|
|
11
|
+
"soft": "#efede8",
|
|
12
|
+
"dark": {
|
|
13
|
+
"bg": "#121316",
|
|
14
|
+
"ink": "#eceae5",
|
|
15
|
+
"muted": "#9a9ca4",
|
|
16
|
+
"line": "#2a2b31",
|
|
17
|
+
"accent": "#7d93ff",
|
|
18
|
+
"panel": "#1a1b1f",
|
|
19
|
+
"soft": "#222328"
|
|
20
|
+
},
|
|
21
|
+
"scheme": "auto",
|
|
22
|
+
"button": "ink",
|
|
23
|
+
"radius": "round",
|
|
24
|
+
"elevation": "soft",
|
|
25
|
+
"density": "cozy",
|
|
26
|
+
"headingFont": "'Geist', ui-sans-serif, system-ui, sans-serif",
|
|
27
|
+
"bodyFont": "'Geist', ui-sans-serif, system-ui, sans-serif",
|
|
28
|
+
"googleFonts": "family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500",
|
|
29
|
+
"width": 720,
|
|
30
|
+
"wideWidth": 1080,
|
|
31
|
+
"header": "floating",
|
|
32
|
+
"footer": "columns",
|
|
33
|
+
"blogLayout": "auto"
|
|
16
34
|
}
|
|
17
35
|
}
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
/* Night Studio —
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
.
|
|
5
|
-
.post-cover { background: var(--soft); }
|
|
6
|
-
a.tag:hover { color: #14151a; }
|
|
7
|
-
.w-btn.primary { color: #14151a; }
|
|
1
|
+
/* Night Studio — personality beyond tokens (the old dark-mode patches are
|
|
2
|
+
gone: accent text, button ink and shadows now come from the tokens). */
|
|
3
|
+
h1, h2, h3 { letter-spacing: -0.03em; }
|
|
4
|
+
.brand { font-family: var(--font-heading); letter-spacing: -0.03em; }
|
|
@@ -1,17 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "Night Studio",
|
|
3
|
-
"description": "Dark charcoal
|
|
3
|
+
"description": "Dark-first studio: charcoal surfaces, Space Grotesk display and a warm amber accent. Set scheme to auto to also offer its light palette.",
|
|
4
4
|
"tokens": {
|
|
5
|
-
"bg": "#
|
|
6
|
-
"ink": "#
|
|
7
|
-
"muted": "#
|
|
8
|
-
"line": "#
|
|
9
|
-
"accent": "#
|
|
10
|
-
"panel": "#
|
|
11
|
-
"soft": "#
|
|
5
|
+
"bg": "#f6f3ec",
|
|
6
|
+
"ink": "#1c1a16",
|
|
7
|
+
"muted": "#6a655b",
|
|
8
|
+
"line": "#e4ded2",
|
|
9
|
+
"accent": "#b87512",
|
|
10
|
+
"panel": "#fdfbf6",
|
|
11
|
+
"soft": "#eee9df",
|
|
12
12
|
"headingFont": "'Space Grotesk', system-ui, sans-serif",
|
|
13
13
|
"bodyFont": "'Inter', system-ui, sans-serif",
|
|
14
|
-
"googleFonts": "family=Space+Grotesk:wght@500;700&family=Inter:wght@400;500;600",
|
|
15
|
-
"width": 760
|
|
14
|
+
"googleFonts": "family=Space+Grotesk:wght@500;600;700&family=Inter:wght@400;500;600&family=Geist+Mono:wght@400;500",
|
|
15
|
+
"width": 760,
|
|
16
|
+
"dark": {
|
|
17
|
+
"bg": "#121316",
|
|
18
|
+
"ink": "#ebe9e3",
|
|
19
|
+
"muted": "#9b9ba4",
|
|
20
|
+
"line": "#2a2b32",
|
|
21
|
+
"live": "#ff6a55",
|
|
22
|
+
"accent": "#e8a849",
|
|
23
|
+
"panel": "#1a1b21",
|
|
24
|
+
"soft": "#23242b"
|
|
25
|
+
},
|
|
26
|
+
"scheme": "dark",
|
|
27
|
+
"button": "accent",
|
|
28
|
+
"radius": "round",
|
|
29
|
+
"elevation": "lifted",
|
|
30
|
+
"displayWeight": 650,
|
|
31
|
+
"displayTracking": "-0.03em",
|
|
32
|
+
"header": "floating",
|
|
33
|
+
"footer": "columns"
|
|
16
34
|
}
|
|
17
35
|
}
|