# AtlantisThemeContext

Provides a way to control the theme of Atlantis components and the design
tokens.

## Design & usage guidelines

Both the web and mobile components have the exact same API, except for one minor
difference in how you update the theme.

Each platform provides a `useAtlantisTheme` hook that you may use to access the
`theme` and `tokens` in your components.

On mobile, this hook also returns a `setTheme` function which you'll use to
update the theme for the nearest `AtlantisThemeContextProvider` ancestor.
Typically there will only be a single provider at the root, controlling the
theme for the entire app.

On web, you'll need to import the `updateTheme` function and call it with the
new theme. This is a separate function because it synchronizes the theme update
across all providers under various React trees. Synchronizing across providers
is necessary for cases where an island-based architecture is used.

### Usage for web

```tsx
import {
  AtlantisThemeContextProvider,
  updateTheme,
  useAtlantisTheme,
} from "@jobber/components/AtlantisThemeContext";

function App() {
  return (
    <AtlantisThemeContextProvider>
      <ThemedComponent />
    </AtlantisThemeContextProvider>
  );
}

function ThemedComponent() {
  const { theme, tokens } = useAtlantisTheme();
  return (
    <Content>
      <div
        style={{
          background: tokens["surface-background"],
          padding: tokens["space-base"],
        }}
      >
        <Content>
          <Text>The current theme is: {theme}.</Text>
          <Text>
            The javascript tokens can be accessed via the tokens object.
          </Text>
          <Text>The theme can be changed using `updateTheme`</Text>
          <Button
            onClick={() => updateTheme("light")}
            label="The theme can be changed to light"
          />
          <Button
            onClick={() => updateTheme("dark")}
            label="The theme can be changed to dark"
          />
        </Content>
      </div>
    </Content>
  );
}
```

### Usage for mobile

```tsx
import {
  AtlantisThemeContextProvider,
  useAtlantisTheme,
} from "@jobber/components/AtlantisThemeContext";

function App() {
  return (
    <AtlantisThemeContextProvider>
      <ThemedComponent />
    </AtlantisThemeContextProvider>
  );
}

function ThemedComponent() {
  const { theme, tokens, setTheme } = useAtlantisTheme();
  return (
    <Content>
      <View
        style={{
          background: tokens["surface-background"],
          padding: tokens["space-base"],
        }}
      >
        <Content>
          <Text>The current theme is: {theme}.</Text>
          <Text>
            The javascript tokens can be accessed via the tokens object.
          </Text>
          <Text>The theme can be changed using `setTheme`</Text>
          <Button
            onPress={() => setTheme("light")}
            label="The theme can be changed to light"
          />
          <Button
            onPress={() => setTheme("dark")}
            label="The theme can be changed to dark"
          />
        </Content>
      </View>
    </Content>
  );
}
```

### Forcing a theme for an AtlantisThemeContextProvider

In some scenarios you may want to force a theme for specific components
regardless of the main application theme. This can be done by setting the
`dangerouslyOverrideTheme` prop to `<themeToSet>` on the
`AtlantisThemeContextProvider`.

Note: If you're using `buildThemedStyles`, any `useStyles` calls must be done
within a *child component* of the `AtlantisThemeContextProvider`, not in the
same component that renders the provider. Same goes for `useAtlantisTheme`.

```tsx
import {
  AtlantisThemeContextProvider,
  updateTheme,
  useAtlantisTheme,
} from "@jobber/components/AtlantisThemeContext";

function App() {
  return (
    <AtlantisThemeContextProvider>
      <ThemedComponent />
      <AtlantisThemeContextProvider dangerouslyOverrideTheme="dark">
        <Text>These components will always be dark themed</Text>
        <ThemedComponent />
      </AtlantisThemeContextProvider>
    </AtlantisThemeContextProvider>
  );
}

function ThemedComponent() {
  const { theme, tokens } = useAtlantisTheme();
  return (
    <Content>
      <div
        style={{
          background: tokens["surface-background"],
          padding: tokens["space-base"],
        }}
      >
        <Content>
          <Text>The current theme is: {theme}. </Text>
          <Text>
            The javascript tokens can be accessed via the tokens object.
          </Text>
          <Text>The theme can be changed using `updateTheme`</Text>
          <Button
            onClick={() => updateTheme("light")}
            label="The theme can be changed to light"
          />
          <Button
            onClick={() => updateTheme("dark")}
            label="The theme can be changed to dark"
          />
        </Content>
      </div>
    </Content>
  );
}
```

### Overriding theme tokens (Web Only)

If you need to override theme tokens, you can do so by supplying the
`dangerouslyOverrideTokens` prop to the `AtlantisThemeContextProvider`. You can
also supply custom tokens which are exposed as CSS variables to any elements
under this `AtlantisThemeContextProvider`. All overridden tokens are also
accessible via the `overrideTokens` object from the `useAtlantisTheme` hook.

This capability allows you to create custom theme providers.

```tsx
import {
  AtlantisThemeContextProvider,
  updateTheme,
  useAtlantisTheme,
} from "@jobber/components/AtlantisThemeContext";

function App() {
  return (
    <AtlantisThemeContextProvider
      dangerouslyOverrideTokens={{
        "color-text": "fuchsia",
      }}
    >
      <Text>This text will be fuchsia!</Text>
    </AtlantisThemeContextProvider>
  );
}
```

### Creating themed styles with buildThemedStyles (Mobile Only)

The `buildThemedStyles` utility is available to help create themed StyleSheets
that automatically update when the theme changes.

```tsx
// In the .style.ts file
import { buildThemedStyles } from "@jobber/components-native";

const useStyles = buildThemedStyles(tokens => ({
  container: {
    backgroundColor: tokens["color-surface"],
    padding: tokens["space-base"],
    borderRadius: tokens["radius-base"],
  },
  text: {
    color: tokens["color-text"],
    fontSize: tokens["font-size-base"],
  },
}));

// In the component file
function ThemedComponent() {
  const styles = useStyles();
  return (
    <View style={styles.container}>
      <Text style={styles.text}>Themed content</Text>
    </View>
  );
}
```

Key features:

* Automatically updates styles when the theme changes
* Works with React Native's StyleSheet system
* Memoized so that the StyleSheet is only re-created when the theme changes


## Props

### Web

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `children` | `ReactNode` | Yes | — | The children to render. |
| `dangerouslyOverrideTheme` | `Theme` | No | — | Force the theme for this provider to always be the same as the provided theme. Useful for sections that should remain... |
| `dangerouslyOverrideTokens` | `OverrideTokens` | No | — | Overrides existing design tokens with custom values. Can also supply custom tokens which will be accessible via useAt... |
