# Conventions

When building and consuming Capra components, we follow specific conventions to maintain a healthy codebase.

### Semantic Properties

Certain properties of Capra components have well-defined, consistent uses.

| Prop         | Purpose                                                                                          | Possible Values                                                |
| ------------ | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------- |
| `appearance` | Change the colors of a component using semantic intent, e.g. a danger `Button` when deleting     | `default`, `info`, `danger`, `warning`, `success`, `highlight` |
| `color`      | Change the color of a component to a specific color, e.g. a teal `Ribbon`                        | Component-specific                                             |
| `layout`     | Alter the component's layout, e.g. a horizontal input field with label                           | Component-specific                                             |
| `size`       | Specify the size of the component                                                                | `xs`, `sm`, `md`, `lg`, `xl`                                   |
| `variant`    | Specify a different visual treatment for the component, e.g. a dot `Badge` vs. a counter `Badge` | Component-specific                                             |

For props where the possible values are pre-defined, a component may utilize the full set or a subset of the possible values.

### Styling Overrides

Capra components are built according to our design system specifications. They should rarely need to be styled differently than what is provided out-of-the-box. In the event custom styling is required, we provide a consistent escape hatch across all of our components for passing a CSS class name: `FORCE__className`.

In your application, you can apply a style override like this:

```tsx
import { Text } from '@capra/core';
import styles from './Component.modules.css';

export function Component(props) {
  return <Text FORCE__className={styles.text}>Hello world</Text>;
}
```

Note that using this prop should be a rarity. Adding styles here is also somewhat brittle. While we do provide this escape hatch, we make no guarantees that the styles applied will apply correctly across version updates, e.g. a style override targeting a specific HTML structure within a Capra component breaks because the component was refactored to use a different HTML structure. The inner workings of Capra components are implementation details and this escape hatch gives one access to those implementation details. Please use it responsibly.

## Client-side Routing

Capra components such as `Link` and `ButtonLink` render links that perform navigation when the user interacts with them. Each component that supports link behavior accepts an `href` prop, which is passed to the underlying `<a>` element.

By default, links perform native browser navigation when they are interacted with. However, Capra supports overriding this behavior using a client-side router of your choice. Set this up once in the root of your app, and any Capra link component will automatically navigate using your router.

Note that external links to different origins will not trigger client side routing, and will use native browser navigation. Additionally, if the link has a target other than `"_self"`, uses the `download` attribute, or the user presses modifier keys such as `Command` or `Alt` to change the default behavior, browser native navigation will occur instead of client side routing.

### `RouterProvider`

The `RouterProvider` component accepts two props: `navigate` and `useHref`. `navigate` should be set to a function received from your router for performing a client side navigation programmatically. `useHref` is an optional prop that converts a router-specific href to a native HTML href, e.g. prepending a base path. The following example shows the general pattern. Framework-specific examples are shown below.

```tsx
import {RouterProvider} from '@capra/core';
import {useNavigate, useHref} from 'your-router';

function App() {
  const navigate = useNavigate();

  return (
    <RouterProvider navigate={navigate} useHref={useHref}>
      {/* ... */}
    </RouterProvider>
  );
}
```

### Router Options

All Capra link components accept a `routerOptions` prop, which is an object that is passed through to the client side router's `navigate` function as the second argument. This can be used to control any router-specific behaviors, such as scrolling, replacing instead of pushing to the history, etc.

```tsx
<Link href="/login" routerOptions={{replace: true}}>{/* ...*/}</Link>
```

When using TypeScript, you can configure the `RouterConfig` type globally so that all link components have auto complete and type safety using a type provided by your router.

```tsx
import type {RouterOptions} from 'your-router';

declare module '@capra/core' {
  interface RouterConfig {
    routerOptions: RouterOptions
  }
}
```

### React Router

The [useNavigate](https://reactrouter.com/en/main/hooks/use-navigate) hook from `react-router-dom` returns a `navigate` function you can pass to `RouterProvider`. The [useHref](https://reactrouter.com/en/main/hooks/use-href) hook can also be provided if you're using React Router's `basename` option. Ensure that the component that calls `useNavigate` and renders `RouterProvider` is inside the router component (e.g. `BrowserRouter`) so that it has access to React Router's internal context. The React Router `<Routes>` element should also be defined inside Capra's `<RouterProvider>` so that links inside the rendered routes have access to the router.

```tsx
import {BrowserRouter, useNavigate, useHref, type NavigateOptions} from 'react-router-dom';
import {RouterProvider} from '@capra/core';

declare module '@capra/core' {
  interface RouterConfig {
    routerOptions: NavigateOptions
  }
}

function App() {
  const navigate = useNavigate();

  return (
    <RouterProvider navigate={navigate} useHref={useHref}>
      {/* Your app here... */}
      <Routes>
        <Route path="/" element={<HomePage />} />
        {/* ... */}
      </Routes>
    </RouterProvider>
  );
}

<BrowserRouter>
  <App />
</BrowserRouter>
```

### Next.js App Router

The [useRouter](https://nextjs.org/docs/app/api-reference/functions/use-router) hook from `next/navigation` returns a router object that can be used to perform navigation. `RouterProvider` should be rendered from a client component at the root of each page or layout that includes Capra links. You can create a new client component for this, or combine it with other top-level providers as described in the [Next.js docs](https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns#using-context-providers).

```tsx
// app/provider.tsx
"use client";

import {useRouter} from 'next/navigation';
import {RouterProvider} from '@capra/core';

declare module '@capra/core' {
  interface RouterConfig {
    routerOptions: NonNullable<Parameters<ReturnType<typeof useRouter>['push']>[1]>
  }
}

export function ClientProviders({children}) {
  const router = useRouter();

  return (
    <RouterProvider navigate={router.push}>
      {children}
    </RouterProvider>
  );
}
```

Then, in your page or layout server component, wrap your app in the `ClientProviders` component that you defined.

```tsx
// app/layout.tsx
import {ClientProviders} from './provider';

export default function RootLayout({children}) {
  return (
    <html>
      <body>
        <ClientProviders>{children}</ClientProviders>
      </body>
    </html>
  );
}
```

### Next.js Pages Router

The [useRouter](https://nextjs.org/docs/pages/api-reference/functions/use-router) hook from `next/router` returns a router object that can be used to perform navigation. `RouterProvider` should be rendered at the root of each page that includes Capra links, or in `pages/_app.tsx` to add it to all pages.

```tsx
// pages/_app.tsx
import type { AppProps } from 'next/app';
import {useRouter, type NextRouter} from 'next/router';
import {RouterProvider} from '@capra/core';

declare module '@capra/core' {
  interface RouterConfig {
    routerOptions: NonNullable<Parameters<NextRouter['push']>[2]>
  }
}

export default function MyApp({Component, pageProps}: AppProps) {
  const router = useRouter();

  return (
    <RouterProvider navigate={(href, opts) => router.push(href, undefined, opts)}>
      <Component {...pageProps} />
    </RouterProvider>
  );
}
```