Skip to content

Latest commit

 

History

History

README.md

BlueDot Website

Quick links:

Tech Stack

  • Framework: Next.js (React)
  • Styling: Tailwind CSS
  • Testing: Vitest (Jest) + React Testing Library
  • Component Development: Storybook
  • Deployment: Kubernetes (via GitHub Actions)

Project Structure

Non-exhaustive — only the most-touched paths are listed.

apps/website/
├── src/
│   ├── components/     # Reusable UI components
│   ├── pages/          # Next.js pages and API routes (pages router)
│   ├── lib/
│   │   ├── api/        # Server-side API helpers (auth, env, db, route wrappers)
│   │   └── ...         # Client/shared helpers (routes, utils, fonts, schemas)
│   ├── hooks/          # Custom React hooks
│   ├── server/         # Server-only modules
│   ├── stores/         # Zustand stores
│   ├── utils/          # Misc client-side utilities
│   ├── types/          # Shared TypeScript types
│   ├── middleware.ts   # Next.js middleware
│   ├── instrumentation.ts # Next.js instrumentation hook
│   └── globals.css     # Custom Tailwind CSS styles
├── public/             # Static assets (incl. /fonts)
└── scripts/            # Build-time scripts (sitemap, preview rendering)

Developer Setup

No special actions needed, just follow the general developer setup instructions.

Common scripts (run from apps/website/):

npm run start         # next dev on http://localhost:8000
npm run build         # next build (also generates sitemap)
npm run typecheck     # tsc --noEmit
npm run lint          # eslint (no warnings allowed)
npm run lint:fix      # eslint --fix
npm run test          # vitest --run
npm run test:watch    # vitest in watch mode
npm run test:update   # update vitest snapshots
npm run render-preview # render OG/preview images

Common Development Tasks

Adding a New Page

  1. Create a new file in src/pages/
  2. Add the route to the nginx template in website-proxy
  3. Update the navigation if needed
  4. Add tests for the new page

Reusing existing components first

Before writing anything new, check what already exists. We have a real shared library and most marketing-page primitives are already in it.

Where to look:

  • Run cd apps/storybook && npm start (or browse storybook.k8s.bluedot.org).
  • Read libraries/ui/src/index.ts for the canonical exports.

@bluedot/ui cheat sheet:

Need Use
Button or link CTA CTALinkOrButton (variants: primary, secondary, black, outline-black, ghost; sizes: default, small)
Page section / card Section, SectionHeading, Card
Heading / paragraph / link H1H4, P, A (or bluedot-h1 / bluedot-p / bluedot-a utility classes)
Hero (page top) HeroSection + HeroH1 / HeroCTAContainer
Form input Input, Textarea, Select, DatePicker, TimePicker, ToggleSwitch
Modal / drawer / dropdown Modal, BottomDrawerModal, OverflowMenu, Tooltip
Accordion / show-hide Collapsible
Alert / tag / banner Banner, Tag, ErrorSection
Carousel / list / progress SlideList, ProgressDots, Breadcrumbs
Avatars / share FaceTiles, ShareButton, SocialShare
Auth LoginRedirectPage, LoginOauthCallbackPage, useAuthStore, withAuth
Utils cn, addQueryParam, maybePlural, asError

Website-local reusables (in src/components/):

  • MarketingHero — top-of-page hero with image background + Nav (used on /about, /our-community, /join-us, /grants).
  • PageListRow — list row with leading slot + CTA (used on /grants, /career-transition-grant). Compose with PageListGroup for divided lists.
  • Nav/Nav — main site nav (variants: default, transparent).
  • Header — page header wrapper.

If you find yourself duplicating one of these, stop and reuse instead.

Use named tokens/utilities, not raw colour or size literals

The token system is documented in apps/storybook/src/GettingStarted.mdx (browse it via Storybook's "Getting Started" intro page).

Don't write Write instead
text-[#0037ff], bg-[#f5f5f5] (raw hex) a named palette utility, e.g. text-bluedot-navy, bg-bluedot-normal
bg-color-primary-accent, border-color-divider (semantic tokens) the palette utility it aliases: bg-bluedot-normal, border-charcoal-light, text-bluedot-darker
text-[16px], text-[18px], text-[24px] text-size-sm, text-size-md, text-size-lg

Prefer the named bluedot-* palette utilities (text-bluedot-navy, including opacity like text-bluedot-navy/60) over the semantic color-* tokens. Size tokens (text-size-*) are still preferred over raw px, though exact pixel values for spacing (e.g. gap-[3px]) are sometimes OK.

Creating a New Component

  1. Create the component .tsx file in src/components/
  2. Add a Storybook story .stories.tsx — autodocs tag, at least 2 variants, args/argTypes
  3. Write tests in .test.tsx and run snapshots via npm run test:update
  4. Use Tailwind classes for styling and BEM classes for identification
  5. Add a parameters.design.figma URL pointing at the Figma node (see CTALinkOrButton.stories.tsx for an example)

Making API Changes

New backend endpoints should be tRPC routers in src/server/routers/. Don't add pages/api/* routes unless tRPC genuinely won't fit (e.g., non-JSON responses like .ics files, cookie-based auth flows, or fire-and-forget error reporters). The few remaining pages/api/ routes are legacy or special-purpose — see DEVELOPMENT_HANDBOOK.md §4.2 for the list.

  1. Add a tRPC procedure to the relevant router in src/server/routers/
  2. For the rare non-tRPC case, use makeApiRoute from src/lib/api/ as the wrapper
  3. For database access, use the shared @bluedot/db package — don't bypass it

Storybook

We use Storybook to develop and test UI components.

To run the Storybook app locally:

# from the root of the repo
npm run start --workspace=apps/storybook

Our storybook is deployed to https://storybook.k8s.bluedot.org.

Production Proxy

Traffic to the production site (https://bluedot.org) is routed through the website-proxy app.

Important: When adding a new page, you must also add it to the nginx template in website-proxy.

Troubleshooting

Common Issues

  1. Build fails locally

    • Clear the .next directory: rm -rf .next
    • Delete node_modules: rm -rf node_modules
    • Reinstall dependencies: npm install
  2. Storybook not loading

    • Ensure you're running from the root directory
    • Check if the Storybook workspace is properly configured

Deployment

This app follows a multistage deployment process. For more details, see docker-scripts.

Staging

To deploy to staging, simply merge a PR into the master branch.

Production

To deploy to production:

  1. Create a new GitHub release
  2. Click "Choose a tag" and enter a new tag in the format website/vX.Y.Z (e.g., website/v1.2.3)
  3. Select "Previous tag" and choose the previous release tag (e.g., website/v1.2.2)
  4. Click "Generate release notes", and review the notes
  5. Click "Publish release"

The production release workflow is defined in .github/workflows/website_deploy_production.yaml.

Note: Please use SemVer for version numbering.

Fonts

The website uses Inter for body text and Inter Display for headlines, while other apps in the monorepo continue to use the shared UI library fonts (Roobert and Reckless Neue).

Implementation Details

  • Font files are loaded via next/font/local from /public/fonts/
  • CSS variable overrides in globals.css remap the shared UI library font variables

⚠️ Important: Font Files Must Be Preserved

Do not delete any font files from /public/fonts/ - here's why:

  • The shared UI library (/libraries/ui/) defines font URLs pointing to https://bluedot.org/fonts/*
  • 8+ other apps (Storybook, Meet, Room, Editor, etc.) load Roobert/Reckless fonts from these URLs
  • Deleting these fonts would break typography in all other apps
  • The website overrides fonts locally while serving them for other apps

Font Inventory

Legacy fonts (used by other apps via HTTPS):

  • Roobert-*.woff2 - Sans-serif (weights: 300, 400, 600, 700)
  • RecklessNeue-*.woff2 - Serif (weights: 300, 300i, 700)

These are loaded by the shared UI library (libraries/ui/src/default-config/tailwind.css) and consumed by the other apps in this monorepo:

  • storybook — component documentation
  • meet — meeting attendance + Zoom Web SDK
  • room — meeting/collaboration app
  • editor — content editor
  • availability — scheduling/availability forms
  • course-demos — interactive course demos
  • frontend-example — example app
  • app-template — template for new apps

Website-specific fonts (loaded via next/font):

  • Inter-*.woff2 - Body text (weights: 400, 500, 600, 700)
  • InterDisplay-*.woff2 - Headlines (weights: 400, 500, 600, 700)
  • Inter-OFL.txt - SIL Open Font License for Inter fonts

For complete font licensing information, see /public/fonts/README.md

Need Help?