Website for Islamskole Bærum, a Sunday Islamic school in Bærum, Norway.
- Next.js 16.2.9 with App Router and Turbopack
- React 19
- Tailwind CSS v4
- shadcn/ui for components
- next-intl v4 for Norwegian/English i18n (locale prefix
/no,/en) - Supabase for database, auth and file storage
npm install
npm run devThe app runs at http://localhost:3000. The middleware redirects / to /no automatically.
You also need a local Supabase stack running:
supabase startOn first run Docker pulls images - this takes a few minutes. After that, create .env.local:
NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY=<anon key from `supabase status --output env`>
SUPABASE_SERVICE_ROLE_KEY=<service_role key from `supabase status --output env`>
To reset the local database and re-apply all migrations including seed data:
supabase db resetAll design tokens live in one place: src/app/globals.css. Edit the CSS variables under :root and .dark to change the color scheme across the entire site.
These steps must be done by the site owner when deploying to production.
Go to supabase.com, create a new project and note the Project URL, anon key and service_role key from Project Settings > API.
Add these to your hosting provider (e.g. Vercel):
NEXT_PUBLIC_SUPABASE_URL=https://<project-ref>.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=<anon key>
SUPABASE_SERVICE_ROLE_KEY=<service_role key>
supabase link --project-ref <project-ref>
supabase db pushThis applies all migrations in supabase/migrations/ to the cloud database, including the schema and seed data.
In the Supabase dashboard, go to Authentication > Settings and set Enable sign ups to off. This ensures only admin users you create explicitly can log in.
In the Supabase dashboard, go to Authentication > Users and click Add user. The on_auth_user_created trigger automatically inserts a row in profiles with role = 'admin', so any user created this way becomes an admin immediately.
After making schema changes on cloud, regenerate types locally:
supabase gen types typescript --linked > src/lib/supabase/types.tsFor local changes, use:
supabase gen types typescript --local > src/lib/supabase/types.tsThe media storage bucket is public for reads. Admins can upload, update and delete files. Bucket is created by the migration so no manual setup is needed.
All illustrations share one flat-vector style defined in scripts/generate-image.mjs (the IMAGE_STYLE constant). Generate a new on-style image with a Gemini API key:
GEMINI_API_KEY=... node scripts/generate-image.mjs "<subject>" <aspect> <output>
Examples:
GEMINI_API_KEY=... node scripts/generate-image.mjs "children reading the Quran together" "4:3" public/brand/example.png
GEMINI_API_KEY=... node scripts/generate-image.mjs "diverse children playing outside a Nordic school with a mosque dome" "1:1" public/brand/hero.png
Aspect ratios used in the UI: hero is 1:1, class cards 4:3, event cards 16:9. Only describe the subject, the shared style is added automatically so every image matches.