Config reference
Portal configuration lives in two places:
portal.config.mjs— server-side runtime config (auth, storage, deploy status). Read by the Astro integration at build time and at SSR request time. Custom section types are not configured here — they register from a rootsrc/sections.tsfile; see Building custom sections.src/content/site-config.json— visual/brand configuration (colours, fonts, media settings). Read by the content loader and shipped as part of the site bundle.
defineConfig options
Section titled “defineConfig options”defineConfig is imported from @drawnagency/core/config (not the root @drawnagency/core export, which includes Node-only integration code).
import { defineConfig } from "@drawnagency/core/config";Source type: PortalConfig in packages/core/src/config.ts.
| Option | Type | Required | Notes |
|---|---|---|---|
auth | AuthProvider | Yes | Authentication implementation. Use supabaseAuth() from @drawnagency/auth-supabase for full OAuth + email flows, or passwordAuth() for the shared-secret fallback. |
storage | StorageProvider | Yes | Content and media storage. Use githubStorage() from @drawnagency/github. Missing storage throws at config evaluation time. |
media | MediaProvider | No | Media serving provider. Defaults to githubMedia() from @drawnagency/core/config. |
deployStatus | DeployStatusProvider | No | Deploy status integration shown in the editor header. supabaseDeployStatus() and netlifyDeployStatus() are exported from @drawnagency/core/config. |
sections | SectionDefinition[] | No | Type-only — not read for registration. Define custom section types in a root src/sections.ts file (see Building custom sections); registration is handled by the virtual:portal/sections channel, which reads that file directly. Passing the same array here only adds a type-check. |
builtins | "core" | "all" | No | Typed contract, not yet wired. Today both built-in groups always register (the generic set + brand-guide: colors, icon_list, dodont_media), so setting this has no effect. "core" is the reserved seam for a future change letting a non-brand-guide site tree-shake the brand-guide group — config.ts notes there is no consumer yet. |
site | { name?: string } | No | Site metadata. name sets the browser tab title; when omitted, the value from site-config.json’s siteName is used. |
Typical portal.config.mjs
Section titled “Typical portal.config.mjs”import { defineConfig, supabaseDeployStatus } from "@drawnagency/core/config";import { supabaseAuth } from "@drawnagency/auth-supabase";import { githubStorage } from "@drawnagency/github";
export default defineConfig({ auth: supabaseAuth(), storage: githubStorage(), deployStatus: supabaseDeployStatus(), site: { name: "Acme Brand Portal" },});site-config.json
Section titled “site-config.json”Stored at src/content/site-config.json. Validated against SiteConfigSchema in packages/primitives/src/schemas/site-config.ts.
Top-level fields
Section titled “Top-level fields”| Key | Type | Default | Notes |
|---|---|---|---|
siteName | string | "Brand Portal" | Display name shown in the nav header |
primaryColor | string | "#009ca6" | 6-digit hex (#rrggbb). Primary brand colour used for interactive elements and accents |
primaryContrast | string | "#f0f0f0" | 6-digit hex. Foreground colour drawn over primaryColor backgrounds |
darkMode | "light" | "dark" | "optional" | "light" | "light" / "dark" fix the colour scheme; "optional" respects prefers-color-scheme |
headingFont | string | "system-ui" | CSS font-family for headings. Allows letters, digits, spaces, commas, quotes, hyphens (max 120 chars) |
bodyFont | string | "system-ui" | CSS font-family for body text. Same character rules as headingFont |
uppercaseHeadings | boolean | true | Apply text-transform: uppercase to link_heading sections |
uppercaseSubheadings | boolean | true | Apply text-transform: uppercase to sub_heading / sub_sub_heading sections |
uppercaseNavHeadings | boolean | true | Apply text-transform: uppercase to nav heading items |
googleFontsUrl | string | null | null | Must start with https://fonts.googleapis.com/. Injected as a <link> in <head> |
favicon | string | null | null | Image data URL (data:image/...). Stored inline so no CDN round-trip is needed |
media sub-object
Section titled “media sub-object”| Key | Type | Default | Notes |
|---|---|---|---|
media.sizes | number[] | [640, 1080, 1920] | Pixel widths for generated WebP variants |
media.maxFileSize | number | 5242880 | Maximum upload size in bytes (default 5 MB) |
media.quality | number (1–100) | 85 | WebP encoding quality |
Note: Some
site-config.jsonfiles include amedia.adapterfield (e.g.,"github"). This field is not part ofMediaConfigSchemaand is silently stripped by Zod. The media adapter is configured inportal.config.mjsvia themediaoption.