Jynx logoJynx

Theme Configuration

Augmenting your theme to suit your needs

Overview

Theming is hardly a one-size-fits-all topic, with as many different ways to use themes as there are people using them. It's for this reason that Jynx remains relatively agnostic when it comes to how you want to define your theme.

There are minimal requirements when specifying your scales and keys, but the rest of the organizational structure is left totally down to you.

Whether your storing a few color values or building out an entire design system, Jynx is flexible enough to allow your theme to morph and scale as your requirements change.

However, there are a few 'best practices' you'll want to be aware of in order to get the most out of your theme, which are detailed below.

Scales

Theme values are defined using scales which can be either an array or an object and allow you to specify a series of associated values that can then be accessed from certain props.

When it comes to then using these values in your components:

  • single-level array-based scale values can be accessed using numbers
  • single-level object-based scale values can be accessed using string-keys
export const theme = {
  colors: {
    red: '#F2335D',
    orange: '#FE8400',
    yellow: '#FFCC00',
    green: '#7CD420',
    blue: '#12A5EC',
    purple: '#7753F8'
  },
  space: [0, 2, 4, 8, 16, 24, 32, 48, 64, 128]
}

/* theme.colors.orange */
<Component bgColor='orange' />

/* theme.space[4] (16) */
<Component m={4} />

Defining values

The way you define the values within your scales is completely up to you, providing you're using valid CSS values (usually string | number).

However, depending on the corresponding props accessing a scale, there are a a few different ways that its values can be interpreted.

Scales with numeric values

The following scales usually specify numeric (often length-based) values:

  • space
  • fontSizes
  • fontWeights
  • lineHeights
  • letterSpacings
  • borderWidths
  • radii
  • zIndices

You can define your values for these scales as strings, numbers, with units, without units or a mixture of the preceding, and they will be parsed accordingly:

  • numbers and numeric strings (i.e. 16 | '16') are converted to px where appropriate
  • strings with units honour the declared unit
  • strings without units resolve to a raw CSS value
  • you can even use a function, as long it returns string | number
export const theme = {
  /* scales can be defined as either an object or an array */
  fontWeights: {
    light: 300,
    regular: 400,
    medium: 500,
    bold: 700
  },
  space: [0, 2, 4, 8, 16, 24, 32, 48, 64, 128],
  /* numbers and numeric strings are converted to `px`
  where appropriate */
  borderWidths: [0, 1, 1.5, 2,], // will be converted
  radii: ['0', '2', '4', '8'], // will be converted
  lineHeights: [300, 400, 500, 700] // will `not` be converted
  /* strings with units honour the defined unit */
  fontSizes: [
    '0.75rem',
    '1rem',
    '1.5rem',
    '2rem',
    '3rem',
    '4rem',
    '5rem'
  ],
  /* strings without units return the raw CSS value*/
  fontWeights: [
    'light',
    'regular',
    'medium',
    'bold',
    ],
  /* you can even use a function (providing the return type
  is {string | number}) */
  letterSpacings: [
    pxToRem(0), // 0rem
    pxToRem(2), // 0.125rem
    pxToRem(4), // 0.25rem
    pxToRem(8) // 0.5rem
  ]
}

Scales with string values

The following scales usually specify string values:

  • colors
  • fonts
  • borders
  • borderStyles
  • shadows
  • transitions

You should ideally define your values for these scales as strings:

export const theme = {
  /* scales can be defined as either an array or an object */
  borders: {
    primary: '1px solid hsla(266, 100%, 65%, 1)',
    active: '1px solid hsla(266, 100%, 65%, 0.5)'
    disabled: '1px solid #eee'
  },
  borderStyles: [ 'solid', 'dashed'],
  /* Any valid string value can be used, meaning you can define
    colors any way you normally would */
  colors: {
    red: 'red', // color keywords
    orange: '#FF8400', // hex values
    yellow: 'rgb(255, 204, 0)', // rgb values
    green: 'hsl(89, 74%, 48%)', // hsl values
    blue: 'rgba(18, 164, 237, 1)', // rgba values
    purple: 'hsla(253, 92%, 65%, 1)' // hsla value
  }
}

Nesting

In addition to defining scales as objects/arrays of strings/numbers you also have the option to nest objects and arrays to create a more complex design system.

This can be useful in situations where you have a larger asset library or just want more granular control over the organization of your theme.

export const theme = {
  colors: {
    neutrals: [
      '#F9FAFB',
      '#F3F4F6',
      '#E5E7EB',
      '#D1D5DB',
      '#9CA3AF',
      '#6B7280',
      '#4B5563',
      '#374151',
      '#1F2937',
      '#111827'
    ],
    accent: {
      primary: 'hsla(126deg, 45%, 44%, 1)',
      secondary: 'hsla(146deg, 50%, 70%, 0.625)'
    },
    brand: {
      purples: ['#622a87', '#7c2eb0', '#9c30e3'],
      pinks: ['#8f1378', '#c204bb', '#e330dd'],
      blues: ['#13828f', '#25abba', '#2dd8eb']
    }
  }
}

/* Access the values through bracket notation */
/* theme.colors.brand.pinks[1] */
<Component color='brand.pinks[1]' />

/* Or through dot notation */
/* theme.colors.neutrals[5] */
<Component bgColor='neutrals.5' />

Cross-referencing

When constructing your theme, you may run into instances where you want to reference one theme value from within another.

An example of this would be wanting to reference the colors scale to specify a border-color in your borders scale.

export const theme = {
  colors: {
    red: '#F2335D',
    orange: '#FE8400',
    yellow: '#FFCC00',
    green: '#7CD420',
    blue: '#12A5EC',
    purple: '#7753F8'
  },
  borders: {
    primary: `1px solid ${this.colors.green}` // this won't work...
  }
}

So how can we do this?

The recommended solution here is to abstract your scales into individual variables and then aggregate these into a single exported theme object.

/* define an individual `colors` scale */
const colors = {
  red: '#F2335D',
  orange: '#FE8400',
  yellow: '#FFCC00',
  green: '#7CD420',
  blue: '#12A5EC',
  purple: '#7753F8'
}

/* define an individual `borders` scale, referencing the `colors` object */
const borders = {
  primary: `1px solid ${colors.green}` // this will work!
}

/* export a global theme object of the combined scales */
export const theme = {
  colors,
  borders
}

This can also be helpful if you want to map existing values to alternate names whilst maintaining a particular theme structure.

/* define an individual `colors` scale */
const scale = {
  red: '#F2335D',
  orange: '#FE8400',
  yellow: '#FFCC00',
  green: '#7CD420',
  blue: '#12A5EC',
  purple: '#7753F8'
}

/* define an individual `elements` scale mapping values to from
  the `scale` object to html elements */
const elements = {
  headings: scale.purple,
  paragraph: scale.red,
  blockquote: scale.orange
}

/* export a global theme object with the two nested scales */
export const theme = {
  colors: {
    scale,
    elements
  }
}

/* and access the value from your component */
/* theme.colors.elements.heading */
<Heading color='elements.heading' />





/* or combine the two scales using ES9 spread syntax and export a
global theme object with a single colors scale */
export const theme = {
  colors: {
    ...scale,
    ...elements
  }
}

/* and access the value from your component */
/* theme.colors.blockquote */
<Blockquote bgColor="blockquote" />

Breakpoints

The breakpoints scale is the most important scale within the theme object and is the only scale to have default values.

/* Default breakpoints */
export const theme = {
  breakpoints: {
    sm: 640,
    md: 960,
    lg: 1280,
    xl: 1440,
    xxl: 1600
  }
}

These values are integral to making responsive styles work (hence why they're predefined) but can easily be customized to work with your own design system.

Defining custom breakpoints

Like any other scale, breakpoints can be defined as an object or an array and can contain string values with units (e.g. '40em') or plain numbers which will be converted to px.

How you chose to define your breakpoints is completely up to you. There's no restrictions on the denomination used or the number of values specified, however, one requirement is that the scale must be only one level deep.

Jynx does not currently have support for nested breakpoint scales.

// numeric, array-based scale
export const theme = {
  breakpoints: [540, 720, 1080, 1440]
}

// numeric, object-based scale
export const theme = {
  breakpoints: {
    mobile: 540,
    tablet: 720,
    laptop: 1080,
    desktop: 1440
  }
}

// string, array-based scale
export const theme = {
  breakpoints: ['40em', '60em', '80em']
}

// string, object-based scale
export const theme = {
  breakpoints: {
    mobile: '40em',
    tablet: '60em',
    laptop: '80em',
    desktop: '100em'
  }
}

Read more about how Jynx uses the breakpoints scale to create mobile-first, responsive styles by visiting the responsive styles page.

Keys

Theme scales are accessed by the key they're assigned too within the theme object, with many style function props having a specific key they use to access their corresponding theme scale.

In most cases, this correlates with the CSS properties it's used for (border-width uses borderWidths, etc), however, certain keys are used with multiple CSS properties when they share they same type of value (margin and padding both use space).

Reference Table

keyproperties
breakpoints
spacegap, column-gap, row-gap, width, height, min-width, min-height, max-width, max-height top, right, left, bottom, margin, margin-top, margin-right, margin-bottom, margin-left, margin-block, margin-block-start, margin-block-end, margin-inline, margin-inline-start, margin-inline-end, padding, padding-top, padding-right, padding-bottom, padding-left, padding-block, padding-block-start, padding-block-end, padding-inline, padding-inline-start, padding-inline-end
fontSizesfont-size
colorsborder-color, border-top-color, border-right-color, border-bottom-color, border-left-color, color, background-color
fontsfont-family
fontWeightsfont-weight
lineHeightsline-height
letterSpacingsletter-spacing
bordersborder, border-top, border-right, border-bottom, border-left
borderStylesborder-style, border-top-style, border-right-style, border-bottom-style, border-left-style
borderWidthsborder-width, border-top-width, border-right-width, border-bottom-width, border-left-width
radiiborder-radius, border-top-left-radius, border-top-right-radius, border-bottom-left-radius, border-bottom-right-radius
shadowsbox-shadow, text-shadow
transitionstransition
zIndicesz-index

Exclusions

There are some CSS properties, such as display or align-items, that only accept a small number of predetermined strings as valid values. Thanks to the type-safe nature of Jynx, your IDE should be aware of these values and provide them to you as suggestions when using the correlating props.

Therefore they should not be included in your theme.

Other props that come under this rule include: overflow, position, justify-content, flex-direction, vertical-align and text-align.

Types

A full rundown of the types associated with the theme can be found on the types page or use the links below to jump to a specific type: