import {ExampleCodeBlock, SymbolDoc} from '@workday/canvas-kit-docs';
import CreateStyles from './examples/CreateStyles';
import CreateVars from './examples/CreateVars';


# Canvas Kit Styling Utilities

A collection of helpful functions for styling with `@workday/canvas-kit-styling`. While they're
fairly simple, they make styling much nicer.

## Pixels to Rem

This function converts a `px` value (number) to `rem` (string). This keeps you from having to do any
tricky mental division or write irrational numbers.

```ts
import {px2rem} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';

const styles = {
  // returns '0.0625rem'
  margin: px2rem(1),
};
```

## Calc Functions

Calc functions are useful for doing basic math operations with CSS `calc()` and variables. They will
also wrap variables automatically in `var()`.

### Add

This function returns a CSS `calc()` addition string.

```ts
import {calc} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';

const styles = {
  // returns 'calc(var(--cnvs-sys-space-x1) + 0.125rem)'
  padding: calc.add(system.space.x1, '0.125rem'),
};
```

### Subtract

This function returns a CSS `calc()` subtraction string.

```ts
import {calc} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';

const styles = {
  // returns 'calc(var(--cnvs-sys-space-x1) - 0.125rem)'
  padding: calc.subtract(system.space.x1, '0.125rem'),
};
```

### Multiply

This function returns a CSS `calc()` multiplication string.

```ts
import {calc} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';

const styles = {
  // returns 'calc(var(--cnvs-sys-space-x1) * 3)'
  padding: calc.multiply(system.space.x1, 3),
};
```

### Divide

This function returns a CSS `calc()` division string

```ts
import {calc} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';

const styles = {
  // returns 'calc(var(--cnvs-sys-space-x1) / 2)'
  padding: calc.divide(system.space.x1, 2),
};
```

### Negate

This function negates a CSS variable to give you the opposite value. This keeps you from having to
wrap the variable in `calc()` and multiplying by `-1`.

```ts
import {calc} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';

const styles = {
  // returns 'calc(var(--cnvs-sys-space-x4) * -1)'
  margin: calc.negate(system.space.x4),
};
```

## keyframes

The `keyframes` function re-exports the [Emotion CSS keyframes](https://emotion.sh/docs/keyframes)
function, but is compatible with a custom Emotion instance and is understood by the Static style
transformer.

### Example

```tsx
import {system} from '@workday/canvas-tokens-web';
import {createComponent} from '@workday/canvas-kit-react/common';
import {
  handleCsProp,
  keyframes,
  createStencil,
  calc,
  px2rem,
  CSProps,
} from '@workday/canvas-kit-styling';

/**
 * Keyframe for the dots loading animation.
 */
const keyframesLoading = keyframes({
  '0%, 80%, 100%': {
    transform: 'scale(0)',
  },
  '40%': {
    transform: 'scale(1)',
  },
});

export const loadingStencil = createStencil({
  base: {
    display: 'inline-flex',
    gap: system.space.x2,
    width: system.space.x4,
    height: system.space.x4,
    fontSize: system.space.zero,
    borderRadius: system.shape.round,
    backgroundColor: system.color.bg.muted.softer,
    outline: `${px2rem(2)} solid transparent`,
    transform: 'scale(0)',
    animationName: keyframesLoading,
    animationDuration: calc.multiply('150ms', 35),
    animationIterationCount: 'infinite',
    animationTimingFunction: 'ease-in-out',
    animationFillMode: 'both',
  },
});

/**
 * A simple component that displays three horizontal dots, to be used when some data is loading.
 */
export const LoadingDot = createComponent('div')({
  displayName: 'LoadingDots',
  Component: ({...elemProps}: CSProps, ref, Element) => {
    return <Element ref={ref} {...handleCsProp(elemProps, loadingStencil())}></Element>;
  },
});
```

## injectGlobal

The `injectGlobal` function re-exports the
[Emotion CSS injectGlobal](https://emotion.sh/docs/@emotion/css#global-styles) function, but is
compatible with a custom Emotion instance and is understood by the Static style transformer. It will also wrap our CSS tokens to ensure you can inject global styles using our CSS variables.

```tsx
injectGlobal({
  ...fonts,
  'html, body': {
    fontFamily: system.fontFamily.default,
    margin: 0,
    minHeight: '100vh',
    ...system.type.heading.large,
  },
  '#root, #root < div': {
    minHeight: '100vh',
  },
});
```

### Example

```tsx
import {createRoot} from 'react-dom/client';
import {fonts} from '@workday/canvas-kit-react-fonts';
import {system} from '@workday/canvas-tokens-web';
import {cssVar, injectGlobal} from '@workday/canvas-kit-styling';
import {App} from './App';

import '@workday/canvas-tokens-web/css/base/_variables.css';
import '@workday/canvas-tokens-web/css/brand/_variables.css';
import '@workday/canvas-tokens-web/css/system/_variables.css';

//@ts-ignore
injectGlobal({
  ...fonts,
  'html, body': {
    fontFamily: cssVar(system.fontFamily.default),
    margin: 0,
    minHeight: '100vh',
  },
  '#root, #root < div': {
    minHeight: '100vh',
    ...system.type.body.small,
  },
});

const container = document.getElementById('root')!;
const root = createRoot(container);
root.render(<App />);
```

## Custom Emotion Instance

Static style injection happens during the parsing stages of the files. This means when you `import`
a component that uses static styling, the styles are injected immediately. This happens way before
rendering, so using the Emotion [CacheProvider](https://emotion.sh/docs/cache-provider) does not
work. A custom instance must be created _before_ any style utilities are called - during the
bootstrapping phase of an application. We don't have a working example because it requires an
isolated application, but here's an example adding a `nonce` to an application:

```tsx
// bootstrap-styles.ts
import {createInstance} from '@workday/canvas-kit-styling';

// assuming this file is being called via a `script` tag and that
// script tag has a `nonce` attribute set from the server
createInstance({nonce: document.currentScript.nonce});

// index.ts
import React from 'react';
import ReactDOM from 'react-dom';

// call the bootstrap in the import list. This has the side-effect
// of creating an instance
import './bootstrap-styles';

import App from './App';

const root = ReactDOM.createRoot(document.querySelector('#root'));

root.render(<App />);

// App.tsx
import React from 'react';

// The following will create and inject styles. We cannot adjust
// the Emotion instance after this import
import {PrimaryButton} from '@workday/canvas-kit-react/button';

// if we call `createInstance` here, we'll get a warning in
// development mode

export default () => {
  return <PrimaryButton>Button</PrimaryButton>;
};
```