> ## Documentation Index
> Fetch the complete documentation index at: https://learn.nexudus.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Styling System

> How the Members Portal styling architecture works, and how to extend or adjust existing components.

The Members Portal uses **Bootstrap 5.3** with SCSS customisation, **CSS Modules** for component-scoped styles, and **CSS custom properties** for runtime theming. This page explains how these layers work together and how to make changes at each level.

## Architecture overview

```
┌──────────────────────────────────────────────────────┐
│  Runtime branding colours (per-location)             │  ← Highest priority
│  Injected via /api/scss-to-css endpoint              │
├──────────────────────────────────────────────────────┤
│  _user.scss                                          │  ← Project-level overrides
├──────────────────────────────────────────────────────┤
│  Component styles (.module.scss + global SCSS)       │
├──────────────────────────────────────────────────────┤
│  custom/ — Bootstrap component extensions            │
├──────────────────────────────────────────────────────┤
│  _variables.scss — Theme colour palette              │
├──────────────────────────────────────────────────────┤
│  _defaults.scss — Base colour defaults               │
├──────────────────────────────────────────────────────┤
│  Bootstrap 5.3 core                                  │  ← Lowest priority
└──────────────────────────────────────────────────────┘
```

Styles are compiled into a single CSS bundle. The compilation order in `style.scss` determines which layer can override which.

## Folder structure

All SCSS source files live under `src/assets/scss/`:

```text theme={null}
src/assets/scss/
├── style.scss              # Main entry – imports everything in order
├── _defaults.scss          # Default colour palette ($primary, $secondary, …)
├── _variables.scss         # Derived theme colours, contrast values, CSS vars
├── _variables-dark.scss    # Dark-mode variable overrides
├── _dark-mode.scss         # Dark-theme component rules
├── _mobile.scss            # Mobile-only responsive overrides
├── _user.scss              # Project-level custom rules (safe to edit)
├── icon.css                # Icon font definitions
├── custom/                 # Bootstrap component extensions
│   ├── _utilities.scss     # Extra utility classes
│   ├── _buttons.scss
│   ├── _card.scss
│   ├── _navbar.scss
│   ├── forms/
│   │   ├── _form-check.scss
│   │   └── _form-control.scss
│   └── …
└── components/             # Global component styles
    ├── _general.scss
    ├── _avatar.scss
    ├── _navbar-mobile.scss
    ├── _sidebar-admin.scss
    ├── _utilities.scss
    ├── _stepper.scss
    └── vendor/             # Third-party library overrides
        ├── dayPicker.scss
        ├── flatpickr.scss
        └── …
```

## Default colour palette

Default colours are defined in `_defaults.scss` using the `!default` flag, which means they can be overridden by any value set before the import:

```scss theme={null}
// _defaults.scss
$primary:   #ff5b13 !default;
$secondary: #0f0d76 !default;
$success:   #008d42 !default;
$danger:    #dc2626 !default;
$warning:   #da7500 !default;
```

`_variables.scss` then derives contrast colours, subtle variants, and border colours from these base values and exports them as a `$theme-colors` map. Bootstrap uses this map to generate CSS custom properties like `--bs-primary`, `--bs-primary-bg-subtle`, etc.

## How components are styled

Components use one of three patterns (often combined):

### CSS Modules (scoped styles)

Component-specific styles live in a `.module.scss` file next to the component. Class names are locally scoped at build time so they never leak.

```tsx theme={null}
// ArkSmallCard.tsx
import styles from './ArkSmallCard.module.scss'

export default function ArkSmallCard({ image }: Props) {
  return <img className={styles.fullImage} src={image} />
}
```

```scss theme={null}
// ArkSmallCard.module.scss
.fullImage {
  height: calc(100% - 100px);
  object-fit: cover;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
}
```

<Tip>
  Use CSS custom properties inside `.module.scss` files to reference theme colours:
  `color: var(--bs-primary);`
</Tip>

### Bootstrap utility classes

Most layout and spacing is handled with Bootstrap 5 utility classes directly in JSX:

```tsx theme={null}
<div className="d-flex justify-content-between align-items-center mt-3 px-4">
  <h5 className="text-truncate fs-5 fw-bold text-body">{title}</h5>
</div>
```

The project extends Bootstrap's utility API in `custom/_utilities.scss` with additional helpers such as fixed-pixel heights (`h-40px`), viewport heights (`vh-100`), and more.

### Global SCSS (unscoped)

Some components import a plain `.scss` file for global styles. These affect the entire page and are typically used for animation keyframes or third-party library overrides:

```tsx theme={null}
import './Typewriter.scss'
```

<Warning>
  Prefer CSS Modules over global imports for new components. Global styles can cause unintended side-effects.
</Warning>

## Runtime branding (per-location colours)

Each location can set its own brand colours in the Nexudus dashboard. The portal loads these at runtime through a two-step process:

1. The client fetches `/api/scss-to-css?businessId=<id>&hash=<colorHash>`.
2. The endpoint compiles a virtual SCSS file that overrides `$primary`, `$secondary`, etc. before importing the full `style.scss`.
3. The compiled CSS is cached in Vercel Blob storage with a hash-based filename.
4. An edge function serves the cached file with CDN headers for fast delivery.

The virtual SCSS that gets compiled looks like this:

```scss theme={null}
$runtime: production;
$primary: #<locationColor>;
$secondary: #<locationColor>;
// … other overrides from the location's colour settings

@import 'style.scss';
```

Because `_defaults.scss` uses `!default`, the runtime values take precedence.

## Dark mode

Dark mode is controlled via the `data-bs-theme` attribute on the `<html>` element. The `_dark-mode.scss` file redefines CSS custom properties when this attribute is set:

```scss theme={null}
[data-bs-theme='dark'] {
  --bs-body-bg: #222529;
  --bs-body-color: #b0b0b8;
  --bs-border-color: rgba(255, 255, 255, 0.07);
  // … full dark palette
}
```

Theme switching is managed by the `useLayoutContext` hook:

```tsx theme={null}
const { updateTheme } = useLayoutContext()
updateTheme('dark')  // 'light' | 'dark' | 'auto'
```

The preference is saved to `localStorage` and restored on load.

<Info>
  When writing component styles, always use CSS custom properties (`var(--bs-body-bg)`) instead of hard-coded colour values. This ensures your styles work in both light and dark modes.
</Info>

## Extending Bootstrap utilities

Custom utility classes are registered in `custom/_utilities.scss` using Bootstrap's utility API. To add a new utility:

```scss theme={null}
// custom/_utilities.scss
$utilities: map-merge(
  $utilities,
  (
    "my-custom-util": (
      property: opacity,
      class: custom-opacity,
      values: (
        25: .25,
        50: .5,
        75: .75,
      )
    ),
  )
);
```

This generates classes like `.custom-opacity-25`, `.custom-opacity-50`, etc. with responsive variants if you add `responsive: true`.

## Overriding Bootstrap component styles

Bootstrap component customisations live in `custom/` as partial SCSS files (e.g., `_buttons.scss`, `_card.scss`). These are imported after Bootstrap core, so they can override default styles.

To adjust a Bootstrap component:

<Steps>
  <Step title="Find the right partial">
    Look in `src/assets/scss/custom/` for an existing file that matches the component (e.g., `_buttons.scss` for buttons).
  </Step>

  <Step title="Add your overrides">
    Write your SCSS rules in that file. You can use Bootstrap variables and mixins:

    ```scss theme={null}
    // custom/_buttons.scss
    .btn {
      border-radius: 0.5rem;
      font-weight: 600;
    }
    ```
  </Step>

  <Step title="Verify the import">
    Make sure the file is imported in `style.scss`. All existing partials are already imported.
  </Step>
</Steps>

## Adding styles to a new component

<Steps>
  <Step title="Create a CSS Module file">
    Add a `.module.scss` file next to your component:

    ```text theme={null}
    src/components/MyComponent/
    ├── MyComponent.tsx
    └── MyComponent.module.scss
    ```
  </Step>

  <Step title="Write scoped styles">
    ```scss theme={null}
    // MyComponent.module.scss
    .wrapper {
      padding: 1rem;
      border: 1px solid var(--bs-border-color);
      border-radius: var(--bs-border-radius);
      background: var(--bs-body-bg);
    }
    ```
  </Step>

  <Step title="Import and use in the component">
    ```tsx theme={null}
    import styles from './MyComponent.module.scss'

    export default function MyComponent() {
      return (
        <div className={styles.wrapper}>
          <p className="text-body fs-6 mb-0">Content</p>
        </div>
      )
    }
    ```
  </Step>
</Steps>

<Tip>
  Combine CSS Modules for component-specific layout with Bootstrap utilities for spacing and typography. This keeps styles scoped while reusing the design system.
</Tip>

## The `_user.scss` file

`_user.scss` is imported last in `style.scss` and is intended for project-level custom rules that don't belong to a specific component or Bootstrap override. It currently contains helpers like `.sticky-top-20`, `.no-select`, and `.disabled-component`.

Add rules here when you need a global utility that doesn't fit into Bootstrap's utility API or a component module.

## Key conventions

| Practice                                             | Rationale                                          |
| ---------------------------------------------------- | -------------------------------------------------- |
| Use CSS Modules for new components                   | Prevents style leaking between components          |
| Use `var(--bs-*)` for colours                        | Ensures light/dark mode and branding compatibility |
| Never hard-code colour values in components          | Runtime branding would not apply                   |
| Put Bootstrap overrides in `custom/`                 | Keeps overrides organized and discoverable         |
| Put vendor/library overrides in `components/vendor/` | Separates third-party concerns from project styles |
| Use `!default` for new SCSS variables                | Allows runtime and theme-level overrides           |
