Styling Strategy
Relevant source files
The following files were used as context for generating this wiki page:
The grapevine_website project employs a modern, utility-first styling strategy centered around Tailwind CSS. This approach facilitates rapid development, ensures visual consistency, and maintains a scalable and maintainable codebase. The core of the strategy involves using Tailwind CSS for direct styling in components, augmented by a theming system using CSS custom properties, a set of primitive UI components, and helper utilities for managing dynamic classes.
The entire application is styled with a custom-loaded font, 'Ambit', which is defined and applied globally, contributing to a unique brand identity.
Sources: README.md, app/layout.tsx:8, 48-51
Core Technologies
The styling architecture is built upon several key npm packages.
Sources: package.json:15-17, 22-29
| Library | Version | Role |
|---|---|---|
tailwindcss | ^4 | The primary utility-first CSS framework. |
clsx | ^2.1.1 | A tiny utility for constructing className strings from an object of keys and booleans. |
tailwind-merge | ^3.4.0 | A utility to intelligently merge Tailwind CSS classes in JS without style conflicts. |
@tailwindcss/postcss | ^4 | PostCSS plugin for Tailwind CSS. |
The clsx and tailwind-merge libraries are typically combined into a cn utility function to handle conditional and conflicting classes gracefully.
The following diagram illustrates how these technologies work together to apply styles to a component.
This diagram shows the flow of generating a final className string using clsx for conditional logic and tailwind-merge to resolve conflicts before applying it to a component.
Theming and Global Styles
A consistent theme is established through global styles and CSS custom properties, ensuring a unified look and feel across the application.
Sources: app/globals.css, app/layout.tsx
CSS Custom Properties (Theme)
The app/globals.css file defines the application's color palette and typography using CSS custom properties within a @theme block. This allows for a centralized and easily configurable design system.
Sources: app/globals.css:3-45
Color Palette
The color system is categorized into brand, base, semantic, border, state, and badge colors.
Brand & Base Colors
| Variable | Value | Description |
|---|---|---|
--color-primary | #1980ef | Main brand color. |
--color-primary-hover | #1570d6 | Hover state for primary elements. |
--color-white | #ffffff | White. |
--color-navy | #0c0e2d | Dark background color. |
--color-light | #f3f7fc | Light background shade. |
Semantic & Border Colors
| Variable | Value | Description |
|---|---|---|
--color-body | #5c6876 | Default text color for body content. |
--color-muted | #9ca9b7 | Muted text color for secondary information. |
--color-border | #e2e8f0 | Default border color. |
--color-border-navy-subtle | #2a3142 | Subtle border color for dark backgrounds. |
State Colors
| Variable | Value | Description |
|---|---|---|
--color-error | #ef4444 | Used for error messages and states. |
--color-success | #10b981 | Used for success messages and states. |
--color-warning | #f59e0b | Used for warnings. |
--color-info | #3b82f6 | Used for informational messages. |
Sources: app/globals.css:5-30
Global Font
The project uses the custom 'Ambit' font, which is loaded locally using next/font/local. This configuration generates a CSS variable --font-ambit and a class name, which are then applied to the <body> tag to ensure the font is used throughout the application.
Sources: app/layout.tsx:8-51, 107
// app/layout.tsx:8-51
const ambit = localFont({
src: [
// ... font file definitions
],
variable: "--font-ambit",
});
// app/layout.tsx:107
<body className={`${ambit.variable} ${ambit.className} antialiased`}>
Base Styles
Global base styles are defined in app/globals.css. This includes setting font-family to use the --font-sans variable (which is mapped to --font-ambit), applying anti-aliasing for smoother text rendering, and setting default background colors.
Sources: app/globals.css:44-60
Component Styling
Styling at the component level follows a utility-first methodology, promoting reusability and maintainability.
Utility-First Approach
Components are styled by applying Tailwind CSS utility classes directly to the className attribute. This is the primary method of styling and is used extensively across all pages and components.
Example from components/footer.tsx:
// components/footer.tsx:19
<footer className="bg-navy py-12 md:py-16 lg:py-18">
<div className="container mx-auto px-6 md:px-12">
<div className="mb-8 grid grid-cols-1 gap-8 sm:grid-cols-3 md:mb-10 md:gap-10 lg:mb-12 lg:grid-cols-5 lg:gap-12">
{/* ... */}
</div>
</div>
</footer>
This snippet demonstrates the use of utilities for background color (bg-navy), padding (py-12), responsive grid layouts (grid, sm:grid-cols-3), and spacing (gap-8).
Sources: components/footer.tsx:19-22
Dynamic and Conditional Classes
For dynamic styling based on component state or props, a cn utility function (a combination of clsx and tailwind-merge) is used. This allows for clean and predictable conditional class application.
An example in components/contact-form.tsx applies a placeholder color only when the select input has no value.
// components/contact-form.tsx:210-217
<Select
id="iAmA"
value={formData.iAmA}
onChange={(e) => handleFieldChange("iAmA", e.target.value)}
error={!!errors.iAmA}
helperText={errors.iAmA}
className={cn(!formData.iAmA && "text-gray-400")}
>
Sources: components/contact-form.tsx:216
Another example in components/featured-resources.tsx shows a function returning different class names based on a prop, which are then applied to a component.
// components/featured-resources.tsx:20-29
const getBadgeStyles = (type: Resource["type"]) => {
switch (type) {
case "Case Study":
return "bg-badge-blue-bg text-badge-blue-text";
case "Report":
return "bg-badge-green-bg text-badge-green-text";
case "Guide":
return "bg-badge-purple-bg text-badge-purple-text";
}
};
Sources: components/featured-resources.tsx:20-29
Prose Styling for Content
For pages with long-form text content, like legal policies, the project uses the @tailwindcss/typography plugin. This is evident from the use of the prose and prose-navy classes, which automatically apply beautiful typographic defaults to plain HTML elements like <h1>, <p>, and <ul>.
Example from app/acceptable-use-policy/page.tsx:
// app/acceptable-use-policy/page.tsx:15
<div className="prose prose-navy">
<h1 className="text-navy mb-6 text-3xl font-bold md:mb-8 md:text-4xl lg:text-5xl">
ACCEPTABLE USE POLICY
</h1>
{/* ... */}
</div>
This approach separates content from its presentation, making it easy to style static content pages.
Sources: app/acceptable-use-policy/page.tsx:15
Responsive Design
Responsive design is implemented using Tailwind's mobile-first breakpoint prefixes (sm:, md:, lg:, xl:). This allows styles to be adapted for different screen sizes directly within the HTML.
Examples can be seen throughout the codebase:
- Layout Spacing: The main content area's top padding changes at different breakpoints.
className="bg-navy pt-20 md:pt-[88px] lg:pt-[104px]"- Sources: app/layout.tsx:108
- Grid Columns: The footer's column count increases on larger screens.
className="grid grid-cols-1 sm:grid-cols-3 lg:grid-cols-5"- Sources: components/footer.tsx:21
- Font Sizes: Heading font sizes are adjusted for different viewports.
className="text-navy mb-6 text-3xl font-bold md:mb-8 md:text-4xl lg:text-5xl"- Sources: app/acceptable-use-policy/page.tsx:16
This consistent use of responsive prefixes ensures the application is usable and aesthetically pleasing across a wide range of devices.