Skip to content

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 root src/sections.ts file; 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 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.

OptionTypeRequiredNotes
authAuthProviderYesAuthentication implementation. Use supabaseAuth() from @drawnagency/auth-supabase for full OAuth + email flows, or passwordAuth() for the shared-secret fallback.
storageStorageProviderYesContent and media storage. Use githubStorage() from @drawnagency/github. Missing storage throws at config evaluation time.
mediaMediaProviderNoMedia serving provider. Defaults to githubMedia() from @drawnagency/core/config.
deployStatusDeployStatusProviderNoDeploy status integration shown in the editor header. supabaseDeployStatus() and netlifyDeployStatus() are exported from @drawnagency/core/config.
sectionsSectionDefinition[]NoType-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"NoTyped 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 }NoSite metadata. name sets the browser tab title; when omitted, the value from site-config.json’s siteName is used.
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" },
});

Stored at src/content/site-config.json. Validated against SiteConfigSchema in packages/primitives/src/schemas/site-config.ts.

KeyTypeDefaultNotes
siteNamestring"Brand Portal"Display name shown in the nav header
primaryColorstring"#009ca6"6-digit hex (#rrggbb). Primary brand colour used for interactive elements and accents
primaryContraststring"#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
headingFontstring"system-ui"CSS font-family for headings. Allows letters, digits, spaces, commas, quotes, hyphens (max 120 chars)
bodyFontstring"system-ui"CSS font-family for body text. Same character rules as headingFont
uppercaseHeadingsbooleantrueApply text-transform: uppercase to link_heading sections
uppercaseSubheadingsbooleantrueApply text-transform: uppercase to sub_heading / sub_sub_heading sections
uppercaseNavHeadingsbooleantrueApply text-transform: uppercase to nav heading items
googleFontsUrlstring | nullnullMust start with https://fonts.googleapis.com/. Injected as a <link> in <head>
faviconstring | nullnullImage data URL (data:image/...). Stored inline so no CDN round-trip is needed
KeyTypeDefaultNotes
media.sizesnumber[][640, 1080, 1920]Pixel widths for generated WebP variants
media.maxFileSizenumber5242880Maximum upload size in bytes (default 5 MB)
media.qualitynumber (1–100)85WebP encoding quality

Note: Some site-config.json files include a media.adapter field (e.g., "github"). This field is not part of MediaConfigSchema and is silently stripped by Zod. The media adapter is configured in portal.config.mjs via the media option.