# Converged Theme
styled helper:
1. rename classNamesMap to classNames
2. write hoc
3. keep in mind recipes pattern
4. tokens
## Vocabulary
**Recipe**: A function which takes a theme (and only a theme) and returns a literal. Recipes can encapsulate complex functions to calculate new colors, fonts, sizes, and other literals. They can chain.
**Token**: A component-specific literal or a recipe which equates to a component specific slot.
**Theme**: An abstract set of knobs which encapsulate the design language and other design system settings.
## Vocabulary Examples
### Recipe
Example of calculating an accessible accent:
```jsx
const accentBackground = (theme) => {
return accessibleColor(
theme.colors.accentPalette, // target
theme.colors.background, // on this background color
1.4 // contrast target
);
}
const accentForeground = (theme) => {
return accessibleColor(
theme.colors.textPalette, // target
accentBackground(theme), // on the accessible background,
1.4 // constrast target
)
}
// Font and Space Recipes (and the logic behind them)
const fontSize = (steps) => {
const {fontSizeBase, fontSizeScale, fontUnit} = theme.fonts; // 1em, 1.2, em
return baseSize * Math.pow(baseScale, steps) + unit;
}
const theme = { //partial
fontSizes: {
sm: fontSize(-1)
}
}
const fontSizeSm(theme) => {
return theme.fontSizes.sm;
}
```
### Tokens
A set of flat variables default values. Tokens are `names` that map to a string literal, or a function of `theme` to literal:
Example:
```jsx
{
tokens: {
thumbBackground: 'red',
background: theme => theme.colors.accentBackground
}
}
```
#### Token Questions/Thoughts
- Should margin/padding/flex be provided inline? (style={{}})
- For 1offs... yes?
- Or, define a css class and use that (className)
- But NOT tokens
### Theme
```jsx
// Theme can contain component tokens as well as colors/typography etc
const theme = {
// not set on globals name
globals: {
typgraphy: {},
spacing: {},
colors: {},
effects: {},
},
components: {
Slider: {
tokens: {
thumbBackground: theme => literal
}
}
}
}
```
## Project plan
We plan to have 2 utilities which separate concerns:
In a new package `@uifabric/merge-styles-react`:
### Styled Function
```jsx
styled(Component)({styles, tokens})
```
#### `styleOptions.tokens`
```jsx
const tokens = (theme) => {
tokens: {
thumbBackground: 'red',
background: theme => theme.colors.accentBackground
}
}
```
#### `styleOptions.styles`
```jsx
const styles = ({theme, tokens}) => {
return {
root: {
background: theme.background
},
label: {IStyle}
}
}
```
#### Styled Function Definition
```jsx
const styled = Component => ({ styles, tokens }) => {
return p => {
const theme = useTheme();
const classNames = resolveClassNames(theme, styles, tokens);
return <Component {...p} classNames={classNames} />;
}
}
```
#### Styled Function Examples
Creating a styled Button off of unstyled BaseButton
```jsx
const buttonStyles = ({theme, tokens}) => {
root: {
background: tokens.background || accentBackground(theme)
fontSize: tokens.fontSize || fontSize(2); // root * scale^2
padding: size(1); // root * scale^1
}
}
const Button = styled(BaseButton)({ buttonStyles, tokens });
```
Creating a variant of an existing styled control
```jsx
const FancyButtonTokens = (theme) => {
return {
background: theme.background
}
}
const FancyButton = styled(Button)
```
#### Static CSS Example
If you just want to simply pass classNames to a component, you don't need styled
```jsx
const SimpleComp = p => Component({...p, classNames: styles });
```
#### A Composite Control
```jsx
const dialogStyles = ({theme, tokens}) => {
root: {
background: tokens.background ||
fontSize: tokens.fontSize || fontSize(2); // root * scale^2
padding: size(1); // root * scale^1
}
}
const Dialog = styled(BaseDialog)({ dialogStyles, tokens });
const Button = styled(BaseButton({ styles, tokens });
// Composed allows you to combine two styled controls together
const FancyDialog = composed(Dialog: { slots: { button: Button } });
```
## Compose Function
The `styled` function is an opionated helper that takes an unstyled components and passes in classNames to the control via a function called in the context of the theme.
> Need Example
```jsx
DialogBase = composed({
slots: {
okButton: 'button',
cancelButton: 'button'
}
});
<Dialog>
<Dialog.Thing>
<Button/>
</Dialog.Thing>
</Dialog>
composed(CommandBar, {
slots: {
item: CommandBarItem
}
});
```
```jsx
const SliderWithSlotOpinions = composed(SliderBase, { slots: {
slots: {
thumb: 'div',
cancelButton: StyledButton
}
}});
1. extend html attributes
2. tokens inline
const SliderStyles: ISliderTokens = {
root: {
background: accentBackground,
backgroundSelected: accent2Background,
'complex selector': {
...
}
}
}
```
### ResolveClassNames
```jsx
const resolveClassNames = (theme, styles, tokens) => {
const classNames = lookup(theme, styles, tokens);
if (!classNames) {
const resolvedTokens = resolve(tokens, theme);
const resolvedStyles =
}
return classNames;
}
```
```jsx
const resolve = (obj, ...args) = {
const newObj = {};
for (let name in obj) {
newObj[name] = (typeof obj[name] ==='function') ? obj[name].apply(this, args) : obj[name];
}
return newObj;
}
```