Documentation Index
Fetch the complete documentation index at: https://mintlify.com/constanza101/borrissol/llms.txt
Use this file to discover all available pages before exploring further.
The Borrissol codebase follows the standard Astro project layout with a few deliberate structural choices: all business and SEO configuration is centralised in src/config/ (split across site.ts, reviews.ts, and seasonal.ts), all translated copy lives in src/i18n/ui.ts, all design tokens live in src/styles/theme.css, and per-language routing is handled by a single [lang]/ dynamic route rather than duplicated per-language directories. Configuration files at the root (astro.config.mjs, keystatic.config.ts, netlify.toml) govern the build, CMS schema, and deployment respectively.
Directory tree
keystatic.config.ts # CMS schema (collections, fields)
astro.config.mjs # i18n, redirects, sitemap, integrations
netlify.toml # Node version + bot-trap redirects
src/
├── assets/images/ # processed by Astro (AVIF/WebP output)
├── components/
│ ├── Seo.astro # all meta, JSON-LD, hreflang
│ ├── Navbar.astro # incl. language switcher + hash preservation
│ ├── HeroSection.astro
│ ├── OfferSection.astro
│ ├── ProcessSection.astro
│ ├── WorkshopsSection.astro
│ ├── TestimonialsSection.astro
│ ├── AboutSection.astro
│ ├── FAQSection.astro
│ ├── CTASection.astro
│ ├── Footer.astro
│ ├── Gallery.astro # honeycomb tap-to-reveal photo grid
│ ├── PressGrid.astro # press page grid
│ ├── PressStrip.astro # press strip on home
│ ├── Button.astro # design-system button primitive
│ ├── WhatsAppFab.astro
│ ├── CookieBanner.astro
│ └── PrivacySection.astro
├── config/
│ ├── site.ts # SEO + business config (SITE object)
│ ├── reviews.ts # Google review data + aggregate rating
│ └── seasonal.ts # time-limited event visibility flags
├── content/
│ └── blog/ # Keystatic-authored MDX posts
├── i18n/
│ ├── ui.ts # all copy, 4 languages
│ └── utils.ts # getLangFromUrl, useTranslations, getAlternatePath
├── layouts/
│ └── Layout.astro # GA + Consent Mode, skip link, favicons, scroll reveal
├── pages/
│ ├── 404.astro # custom 404 page
│ ├── index.astro # / (CA, default locale)
│ ├── gallery.astro # /gallery (CA)
│ ├── press.astro # /press (CA)
│ ├── tufting.astro # /tufting (CA)
│ ├── punch-needle.astro # /punch-needle (CA)
│ ├── felting.astro # /felting (CA)
│ ├── loom.astro # /loom (CA)
│ ├── borla.astro # /borla (CA)
│ ├── summer-lab.astro # /summer-lab (CA)
│ ├── team-building.astro # /team-building (CA)
│ ├── pelussetes.astro # /pelussetes (CA)
│ ├── [lang]/ # dynamic routes — /es, /en, /fr variants
│ │ ├── index.astro
│ │ ├── gallery.astro
│ │ ├── press.astro
│ │ ├── tufting.astro
│ │ ├── punch-needle.astro
│ │ ├── felting.astro
│ │ ├── loom.astro
│ │ ├── borla.astro
│ │ ├── summer-lab.astro
│ │ ├── team-building.astro
│ │ └── pelussetes.astro
│ └── blog/
│ ├── index.astro # /blog
│ └── [slug].astro # /blog/post-slug
└── styles/
└── theme.css # design system tokens
Directory and file reference
src/assets/images/
All content images are stored here and run through the Astro asset pipeline at build time. Astro’s <Picture> component converts sources to AVIF + WebP with a responsive srcset, and automatically generates the correct width/height attributes to prevent Cumulative Layout Shift (CLS = 0). Source files should be no larger than approximately 2× the widest responsive variant that will be served.
The hero image uses loading="eager" and fetchpriority="high" to optimise LCP (1.8 s on mobile). All other images use loading="lazy".
src/components/
Components are organised into four informal categories:
| Category | Directory / Files | Purpose |
|---|
| Page sections | HeroSection.astro, OfferSection.astro, WorkshopsSection.astro, etc. | Full-width bands that compose the single-page landing layout |
| Shared primitives | Button.astro, Seo.astro, Navbar.astro, Footer.astro, CookieBanner.astro | Reusable UI building blocks used across multiple pages |
| Gallery & press | Gallery.astro, PressGrid.astro, PressStrip.astro | Feature-specific display components |
| Utilities | WhatsAppFab.astro, PrivacySection.astro | Single-purpose functional components |
Seo.astro is the single place for all <meta> tags, JSON-LD structured data blocks (LocalBusiness, FAQPage, ImageObject, AggregateRating), Open Graph/Twitter Card tags, hreflang links, and canonical URL. It accepts per-page overrides as props and falls back to the values in src/config/site.ts.
src/config/
The config directory holds three separate modules — not one. Each is a single-purpose export:
| File | Exports | Description |
|---|
site.ts | SITE: SiteConfig | Every SEO token and business detail: name, URL, locale, logo path, OG image path, and the full BusinessConfig (type, address, geo coordinates, telephone, email, opening hours, price range, social links). Seo.astro and the JSON-LD generators consume this object directly. |
reviews.ts | REVIEWS: Review[], REVIEW_AGGREGATE | Real Google review data (reviewer names + i18n keys for review text) and aggregate rating totals (ratingValue, reviewCount). Shared by both TestimonialsSection.astro and the AggregateRating JSON-LD node in Seo.astro. Update REVIEW_AGGREGATE whenever the Google Business Profile totals change. |
seasonal.ts | summerLab: SeasonalEvent, isSummerLabVisible() | Controls whether time-limited promotional cards are shown. Visibility is evaluated at build time (not per request), so a card hides on the first deploy after endDate. Bump the dates each year to re-enable the card. |
Update site.ts rather than touching individual components when business details change. Never hardcode review counts or aggregate ratings inline — always edit reviews.ts.
src/content/blog/
MDX files authored via the Keystatic CMS panel at /keystatic. Posts are processed through Astro Content Collections (getCollection('blog')). The blog is intentionally Catalan-only to keep the editorial workload sustainable — /es/blog, /en/blog, and /fr/blog all 301-redirect to /blog.
src/i18n/
| File | Exports | Description |
|---|
ui.ts | Translation map object | All visible copy for every UI string in all four languages (Catalan, Spanish, English, French). Zero hardcoded strings are allowed in components — every user-facing string references a key from this file. |
utils.ts | getLangFromUrl, useTranslations, getAlternatePath (also called localizedPath) | Helper functions for extracting the active locale from the current URL, retrieving the correct translation map, and generating localised hrefs for the language switcher. |
src/layouts/
Layout.astro is the single shared shell for every page. It injects:
- Google Analytics 4 (
gtag.js) with Consent Mode v2 Advanced — GA loads on every visit in cookieless mode by default; cookies are only granted after the user accepts the cookie banner.
- A skip-to-content link as the first child of
<body> for keyboard accessibility.
- Preconnect hints for third-party domains.
- The scroll-reveal
IntersectionObserver script (respects prefers-reduced-motion).
- Self-hosted Roboto WOFF2 font declarations with
font-display: swap.
src/pages/
Astro’s file-based router maps files to URLs directly. The routing strategy for Borrissol:
- Catalan default —
index.astro, gallery.astro, press.astro, and all workshop pages (tufting.astro, punch-needle.astro, felting.astro, loom.astro, borla.astro, summer-lab.astro, team-building.astro, pelussetes.astro) sit at the top level and are served at their respective root paths. There is no /ca/ prefix.
- Other languages — mirrored files inside
[lang]/ use getStaticPaths to generate the /es, /en, and /fr variants at build time.
- Blog —
blog/index.astro and blog/[slug].astro are Catalan-only. Non-Catalan blog URLs 301-redirect to the Catalan equivalents in astro.config.mjs.
- Seasonal pages —
summer-lab.astro is always built and served; the home-page promotional card that links to it is conditionally rendered based on isSummerLabVisible() from src/config/seasonal.ts.
src/styles/theme.css
The single source of truth for the entire design system. Contains all CSS custom properties on :root:
- Colors —
--color-white, --color-light, --color-mid, --color-muted, --color-black, plus semantic aliases (--bg, --fg, --fg-muted, --border-hairline).
- Typography —
--font-size-h1 through --font-size-h4, --font-size-p1 through --font-size-p3, --font-size-ui, weight, line-height, and letter-spacing tokens.
- Spacing —
--space-xs (8 px) through --space-xl (80 px).
- Border radius —
--radius-pill, --radius-md, --radius-sm, --radius-xs.
- Icons — stroke width, color, and size tokens for each context (inline, button, nav, feature, hero).
- Utility classes —
.text-h1–.text-h4, .text-p1–.text-p3, .text-ui, .text-muted, .eyebrow, .card, .media, .btn, .btn-primary, .btn-secondary, .btn-tertiary, .btn-sm, .badge, .badge-light, .input, .checkbox.
Never hardcode a color, spacing value, font size, or border radius anywhere in the codebase. Always reference a token from theme.css. If a value isn’t tokenized yet, add it to theme.css first, then use it.
keystatic.config.ts
Defines the Keystatic CMS schema — the blog collection, its fields (title, publish date, description, body MDX), and the storage strategy (local Git-backed files in src/content/blog/). In production, Keystatic authenticates via Keystatic Cloud; serverless function invocations only occur when the editor uses the /keystatic UI — regular visitors load fully static pages at zero function cost.
astro.config.mjs
The Astro build configuration. Key settings:
site — set to https://borrissol.com for correct sitemap and canonical URL generation.
adapter — @astrojs/netlify for hybrid rendering (static pages + Netlify functions for the Keystatic panel).
i18n — defaultLocale: 'ca', locales ['ca', 'es', 'en', 'fr'], prefixDefaultLocale: false (Catalan served at root without /ca/ prefix).
redirects — 301 rules for legacy /ca/* URLs and cross-language blog redirects (all /<lang>/blog → /blog).
integrations — @astrojs/sitemap (with hreflang cross-references between language variants), @astrojs/react (required by Keystatic), @astrojs/markdoc, @keystatic/astro.
netlify.toml
Controls the Netlify build and edge behaviour:
[build] — build command npm run build, publish directory dist.
[build.environment] — pins NODE_VERSION = "22.12.0" to match the Astro 6 requirement.
- Bot-trap redirects — a series of forced-404
[[redirects]] rules that short-circuit common WordPress/PHP/admin-tool scanner probes (/wp-admin/*, /wp-login.php, /.env, /.git/*, etc.) at the Netlify edge layer. This prevents bot requests from invoking Astro SSR and consuming function credits.
URL routing reference
| URL pattern | File | Notes |
|---|
/ | src/pages/index.astro | Catalan home (default locale, no prefix) |
/es, /en, /fr | src/pages/[lang]/index.astro | Non-Catalan home pages via getStaticPaths |
/gallery | src/pages/gallery.astro | Catalan gallery |
/es/gallery, /en/gallery, /fr/gallery | src/pages/[lang]/gallery.astro | Localized gallery pages |
/press | src/pages/press.astro | Catalan press page |
/es/press, /en/press, /fr/press | src/pages/[lang]/press.astro | Localized press pages |
/blog | src/pages/blog/index.astro | Blog index (Catalan only) |
/blog/[slug] | src/pages/blog/[slug].astro | Individual blog posts (Catalan only) |
/tufting | src/pages/tufting.astro + src/pages/[lang]/tufting.astro | Tufting workshop landing (CA root + ES/EN/FR via [lang]) |
/punch-needle | src/pages/punch-needle.astro + src/pages/[lang]/punch-needle.astro | Punch needle workshop landing |
/felting | src/pages/felting.astro + src/pages/[lang]/felting.astro | Felting workshop landing |
/loom | src/pages/loom.astro + src/pages/[lang]/loom.astro | Loom weaving workshop landing |
/borla | src/pages/borla.astro + src/pages/[lang]/borla.astro | Borla workshop landing |
/summer-lab | src/pages/summer-lab.astro + src/pages/[lang]/summer-lab.astro | Summer Lab seasonal offering (visibility controlled by src/config/seasonal.ts) |
/team-building | src/pages/team-building.astro + src/pages/[lang]/team-building.astro | Team building group format landing |
/pelussetes | src/pages/pelussetes.astro + src/pages/[lang]/pelussetes.astro | Pelussetes workshop landing |
/keystatic | Keystatic integration | CMS panel (auth-gated in production, open in dev) |
/ca, /ca/* | — | 301 → equivalent root URL (legacy redirect) |
/es/blog, /en/blog, /fr/blog | — | 301 → /blog (blog is intentionally CA-only) |