loading

v33.4.0

select option

CSS Style Guide

How we build visuals for ink

Introduction

Much the same way ink is always changing, so too are our styles. Presently, ink is using Styled Components to build component visuals, and is in the process of adding visual tokens to help with scalability and consistency. These initiatives help not only to avoid the usage of internal APIs outside of ink, but will make it easier to add future visual features like dark mode.

This document outlines some of the rules we have for creating new components, refactoring visual code or iterating on new visuals.

🚨 Attention!

  1. Before adding any code, share your idea on the #ink channel and let us know how we can help you with it!
  2. How to propose changes on ink
  3. As an alternative, you can also send us a message in the #design-technology channel or reference @design-technologists in any channel if you have questions!

Coding standards and best practices

Help your peers by writing code in a way that's easy to be read.

We are fans of linting. Make sure ESLint working on your editor of choice. If you're unfamiliar with code editors and/or linting, please reach out in the ink channel.

Comment your code

  • Add comments if it would be difficult for a developer to understand the reasoning behind it at a first glance
  • Add comments if you use calculations. In complicated cases that utilize formulas you've found online (such as StackOverflow), reference an article
  • When we pass multiple variables and values to rules, or use the calc property, it can be hard to figure out what is being evaluated. Enforcing calculations to be placed inside () makes it easier to read, avoids interpretation mistakes and prevents corrupting near values that shouldn’t be part of the operation

Optimize for maintainability

  • Avoid using !important. If you can't achieve what you want without an !important, let us know in the #ink channel and we'll help you find an alternative
  • Use as many global tokens as possible
  • Make use of existing ink components instead of building one-offs (for example: use Box instead of a div)
  • Order CSS declarations alphabetically unless specificity matters

Internal file structure

Every component should have these files:

  1. Inside the component folder, a styles.ts should house the stylistic choices of a component.
  2. Inside ./src/tokens/components/, a component folder matching the component's name should be added along with a .json5 which stores design choices in the form of tokens. A .tokens.js file will automatically get generated in this same folder after running yarn tokens

Naming conventions

  • Add the name of the component into the styled component name
    • For example, If you need to name a div that wraps around the component, you should name it ComponentWrapper
  • Use semantic names so it's easier for other developers to understand your code when names make sense
    • For example, if your component has an internal input of the checkbox type, you could call it ComponentCheckbox demonstrating the input is a checkbox
  • Use PascalCase for components, and types interfaces
    • For example, Button, ButtonProps or StyledButton
  • Use camelCase for variables, functions and props
    • For example, htmlType, isDisabled, globalProps or buttonCore

Tokens

We're introducing tokens help us scale design decisions by offering abstraction layers that allow for us to have more control over hard coded and primitive values.

There are currently 3 abstraction layers for tokens

All design decisions are stored in json before they get consolidated into tokens by running:

$ yarn tokens

Which generates many .tokens.js files that can be imported and used in a component's styles.ts file.

IMPORTANT .tokens.js files are not be edited directly! If you want to make changes to the tokens you should be doing them in the ./src/tokens/ folder.

Reusable values

Tokens are primarily values that can be reused and are created with visual intention. Reset values for example, should not be tokens since they are not visual choices we've made, but a margin set to 17px might be a good candidate for a token.

Core Tokens

Core tokens store the hardcoded primitive values for properties. They are only accessible inside of ink and should be rarely changed because of their potential to affect the whole library.

  • For example: primitive colors, font sizes and weights, font types and other values that have low volatility

These tokens can be found in the ./src/tokens/core folder

Global Tokens

Global tokens are the main layer of abstraction referenced by components. This layer is responsible for turning the hardcoded tokens into tokens with semantical meaning.

To use core tokens in your global token, find the best nomenclature and add them with dot notation inside quotes and brackets:

{
"color": {
"global": {
"background": {
"danger": { "value": "{color.core.red.base.value}" }
}
}
}
}

These tokens can be found in the ./src/tokens/global folder

Component Tokens

Component tokens are the third layer of abstraction, utilizing global tokens to create consistency throughout ink.

Not all values for properties in the component css should be turned into tokens. Visual choices such as color, font, spacing properties should be a token. Resetting a css value should not unless its an interactive state transition.

{
"button": {
"core": {
"background": {
"color": { "value": "{color.global.background.transparent.value}" }
},
"border": {
"radius": { "value": "{size.global.radius.small.value}" },
"style": { "value": "solid" },
"width": { "value": "{size.global.border.small.value}" },
"color": { "value": "{color.global.background.transparent.value}" }
},
"font": {
"size": { "value": "{font.global.size.body.value}" },
"weight": { "value": "{font.global.weight.medium.value}" }
}
}
}
}

These tokens can be found in the ./src/tokens/global folder

Commenting a token

To add comments in your tokens you just need to add a second key/value pair. When you run yarn tokens this key/value pair will be added as a comment alongside the specific token:

{
"size": {
"global": {
"border": {
"small": { "value": "{size.core.border.1.value}" },
"medium": {
"value": "{size.core.border.3.value}",
"comment": "we will revisit these naming conventions"
}
}
}
}
}

output:

export const sizeTokens = {
GlobalBorderSmall: "1px",
GlobalBorderMedium: "3px", // we will revisit these naming conventions
}

Coding Style

Pseudo-classes

  • You may use pseudo-classes inside your styled components if they are necessary (hover, focus, etc);
  • Don't use pseudo-classes for conditional rendering. Use a prop or a ThemeProvider instead.
const PaginationWrapper = styled.div`
${paginationCore}
${({ theme }) =>
theme.trim &&
css`
margin: 0;
`}
`

Units

We currently use pixels for several reasons:

  1. Tech debt. The html font-size is hardcoded so rem doesn't work as well until we refactor
  2. Our rem values were random, which cause browsers to generate fractional pixels, causing misalignment and bugs

For images, only use SVG images for interface elements.

Is this page helpful?