arrow-left

Only this pageAll pages
gitbookPowered by GitBook
triangle-exclamation
Couldn't generate the PDF for 101 pages, generation stopped at 100.
Extend with 50 more pages.
1 of 100

V2

Getting started

Loading...

Loading...

Loading...

Loading...

Collect

Loading...

Loading...

Loading...

Loading...

Distribute

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Concepts

Loading...

Loading...

Loading...

Loading...

Loading...

Guides

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Reference

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Resources

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

What is Specify?

The first Design Token Engine allowing you to send your design tokens across your design system tools. Specify is built with this idea: configure once, synchronize anytime.

Specify is a central and tool-agnostic platform. It means you can integrate your daily tools to collect, store and distribute your design data to the right teams, in the right format, at the right time. To sum things up, see Specify as the synchronization layer for your design data at the heart of your design system.

  • Designers, connect your favorite design tool, like Figmaarrow-up-right or Tokens Studio, and let Specify monitor and collect your design tokens and assets automatically. This way, you control what is sent to developers and reduce the risk of inconsistencies.

  • Developers, configure tailored transformation pipelines for every project and get design tokens and assets from wherever you want. Use our or get automated Pull Requests. You can also create your custom pipeline thanks to our .

  • Teams can benefit from a single source of truth and a continuous delivery system that ensures brand consistency.

In short, you have more time to focus on what matters most: your products and your end-users.

Introduction

Learn about our API and how to sync and transform design tokens in your design system.

Specify CLI

hashtag
Build for the CI/CD

The Specify CLI enables you to generate your code in any environment. Built on top of the Specify SDK, built with our newly released format (SDTF), and compiled with Bunarrow-up-right, this tool will help you create your CI/CD workflows.

hashtag
Let's jump in!

How to get started? Easy as pie, follow our.

What is a Source?

hashtag
Definition

A Source represents where the original design data come from - think of (Variables or Local Styles) or .

You can add many sources into a .

Learn more about the available compatible with Specify.

Available sources

List of all integrations you can use with Specify to collect design tokens and assets from.

HTTP API

Consume your design tokens and assets over HTTP requests.

hashtag
Access raw data

Using the Specify HTTP API, you access the most barebone data directly out of our generation services. Thus, you can fetch your design tokens, manipulate them programmatically, and integrate them smoothly into any development workflow.

hashtag

Specify SDK

Distribute your design tokens and assets from Specify right from your codebase.

hashtag
You are in Control

The Specify SDK is here to give you more power and freedom toward design token code generation. Built on top of our newly released format (SDTF), this brand new tool will help you build custom solutions tailor-shaped to your design token workflow. In a few words, the SDK provides all the necessary pieces and architecture you will need to ensure that you are in control of your design tokens system delivery. Written in TypeScript, it can also be used in a JavaScript environment.

Available destinations

List of all integrations you can use with Specify to distribute design tokens and assets to.

Work with any language

Your project cannot support JavaScript or TypeScript? Bring your own language and integrate seamlessly with Specify resources.

hashtag
Generate upon request

The HTTP API leverage the Parsers Rules to let you instruct transformations to your SDTF design tokens right when you make the request making sure the data you receive is up to date with your repository.

hashtag
Get started

circle-info

The Specify HTTP API is read-only upon your design data.

To use the HTTP API, you'll need:

  • A Specify account with at least one repository, filled with design data from any source.

  • A personal access token 👉 generate onearrow-up-right.

Jump to the HTTP API section to get started.

hashtag
Leverage the most advanced design token format

By levering your design token graph, the SDK provides all the methods needed to manipulate and transform the data coming from Specify. Whether it is about filtering and renaming tokens, groups and collections, deleting modes in tokens or transforming your data into any languages. Based on the Specify engine and parsers technology, you'll benefit from their flexibility without any maintenance.

hashtag
Built to suit your standards

The idea behind the SDK is also for you to have more freedom on the output. Meaning that if the current parser technology doesn't meet your standards entirely, you can add custom codes among the pipeline chain. Everything is executable locally so you are not tied to a vendor, which allows you to create your own dedicated delivery engine that follows your company standards.

hashtag
Let's jump in!

How to get started? Easy as pie, follow our Getting started section to grasp the best usage of the SDK.

Getting started
hashtag
Collecting the design data

Once a source is configured, Specify starts to collect your design data. Over this process, the SDTF Engine acquires, validates and converts the original data into a SDTF token tree that get stored within the repository.

From this point on, you can synchronize your source(s) with Specify anytime, at a click of a button.

circle-info

You can review your sources status by heading to any repository page on the Specify webapparrow-up-right.

hashtag
Repository with many sources

Data from many sources get automatically merged into a consolidated SDTF token tree. If Specify detects a conflict over sources, it will refuse to synchronize the first source that cause the conflict and turn it into an error state.

Figma
Tokens Studio
repository
Sources
Cover

Figma Variables & Styles

Learn how to import your design tokens from variables and local styles.

Cover

Tokens Studio

Learn how to import your design tokens and themes from your preferred storage.

Getting started

A short guide on collecting and pulling your first design tokens and assets.

Concepts

Learn our terminology and concepts used in Specify.

Community

Join our Discord community to learn and share tips.

Configuration templates

Start in seconds with our ready-to-use config templates.

Tutorials

Watch our YouTube videos to get started with Specify.

CLI
GitHub
SDK

GitHub

Distribute your design tokens and assets via automated Pull Requests.

CLI

Distribute your design tokens and assets from Specify right from your terminal.

Cover

Specify SDK

Distribute your design tokens and assets from Specify right from your codebase.

HTTP API

Consume your design tokens and assets over HTTP requests.

Pulling your first tokens with the CLI

A 5min guide on collecting and pulling your first design tokens with the Specify CLI.

hashtag
Introduction

In this guide you’ll learn how to pull your first design tokens to CSS Custom Properties using the Specify CLI.

circle-info

Want to know more on how to collect design tokens from Figma to Specify first? Head towards the section!

hashtag
Before getting started

To get the most out of this guide, you’ll need:

  • A Specify account

  • A Specify repository containing design tokens ()

hashtag
1. Install the CLI

Install @specifyapp/cli

hashtag
2. Create your Specify config file

Create a configuration file for your desired output format using one of our

hashtag
3. Add your Specify repository

Add your Specify repository and workspace from which you want to pull your design tokens. You can find your workspace name and repository name in the URL of the app when inside a repository.

hashtag
4. Add your personal access token

Generate a personalAccessToken for the CLI and add it in your configuration. You can generate a personal access token from you .

hashtag
5. Set your output format

Add your first rule and use the parser to generate your tokens as CSS variables.

hashtag
6. Pull your design tokens and assets

Our configuration is ready and we can now pull our design tokens and assets using the pull command.

What is a Destination?

hashtag
Definition

A destination belongs to a Repository, it represents where the formatted design data flow on updates - think of GitHub or any automation you can come up with our CLI and SDK.

You can add many destinations into a single repository. Read more about the available Destinations compatible with Specify.

hashtag
Distributing the design data

Once your repository is filled in, you will start to distribute your design data throughout your design system consumers, starting with development teams.

The destinations offer several levels of control over the transformation required to meet your company standards.

Two main APIs at stake here:

  • The , accessible via all , offers an high level abstraction to directly transform any SDTF token tree into many well-known technologies like CSS, tailwind, style-dictionary… Read more about .

  • The , accessible via the , offers a fine grained control over your SDTF token tree by allowing any mutation and post-process hooks, directly in your codebase.

hashtag
Getting started with templates

If you are planning to use the or , you can head over to our that will help you start distributing your token to the main common use cases.

SDTF Engine

Leverage the Specify core APIs to manipulate the SDTF token tree and produce any output you need.

hashtag
Introduction

The SDTF Engine brings together the core APIs that enable most of Specify features to seamlessly integrate, whether you're working in Figma or Token Studio, managing GitHub pull-requests, or directly within your codebase with the SDK or the CLI.

circle-info

You can access the SDTF Engine APIs over the .

circle-check

all parsers written by Specify uses the SDTF Engine under the hood.

hashtag
Token Tree CRUD API

At the core of the SDTF Engine, the query and mutation methods.

hashtag
Query

The SDTF Engine provides a set of built-in getter functions to access tokens, groups, collections and aliases over the loaded token tree.

getAllTokenStates, getChildrenOf, getParentsOf, getAliasReference… - give you the opportunity to traverse and iterate over the different instances of TokenState, GroupState, and CollectionState.

hashtag
Mutation

The SDTF Engine provides a set of built-in mutation functions to locally manipulate the underlaying token tree in order to meet your project and/or company standards.

updateTokenModeValue, updateTokenValue, moveToken, addToken, deleteToken, renameToken… - assemble into a CRUD API giving you the control to bend your data just right for your use cases, without ever making changes to the original design data from the repository.

hashtag
SDTF Query Language

Elevating your control over the data, the SDTF Query Language offers sophisticated techniques to navigate and cherry-pick the token tree with precision. You will find functions that allow complex querying patterns, matching specific criteria and conditions, vital for advanced manipulation and data extraction tasks.

With this powerful language at your disposal, it becomes easy to implement custom scenarios and data transformations.

circle-info

Looking for the SDTF Query Language details? 👉 review the reference

circle-info

The SDTF Query Language is accessible in the and in some via a dedicated option.

hashtag
Stateful Value API

The Stateful Value API grants direct access to examine the values assigned to tokens.

Through an exhaustive map interface, the API simplifies the process of keeping your design tokens and their implementations in sync despite the creation of aliases along the way.

GitHub

Distribute your design tokens and assets via automated Pull Requests.

circle-info

Specify will sync your GitHub repository if you have a config file .specifyrc.json saved at the root of your GitHub repository.

hashtag
Prerequisites

Please make sure you have:

  • A GitHub account

  • A Specify account

  • One or multiple Specify repositories containing some design tokens.

circle-info

Want to connect a GitHub repository from your GitHub organization? Please make sure you have the correct access rights. Otherwise, you'll need an owner to approve your installation request.

hashtag
Connecting Specify and GitHub

Once you've connected your GitHub account, Specify has to know what design tokens to synchronize and how.

  1. Go to the Specify Advanced Repository you want to distribute design data from

  2. Go to its "Destinations" page

  3. Click on "Create Pipeline"

hashtag
Useful resources

SDTF Client

Step into the Specify SDK APIs to get fined grained access to your design data.

hashtag
Introduction

The Parsers Engine produces finite and opinionated outputs, but sometimes you need to have more control over our generated files.

That's where the SDTF Client takes over and provide plenty of methods to work with any SDTF token tree.

circle-info

Looking for how to get started with the SDK? 👉 heads up to the guide.

hashtag
Overview

The SDTF Client offers versatile methods for working with token trees, enabling data manipulation, transformation, and conversion into various formats or languages, bridging the gap between stored and live token instances within your repository.

To access your data, you use the . You can then fetch a and use the to manipulate the data and transform to your desired custom format.

The SDTF Client API wraps up the which holds the lower level methods to work with the token tree.

hashtag
The Specify Client API

The Specify Client manages:

  • your authentication credentials - personal access token

  • your access to your organization repositories

hashtag
The SDTF Client API

The SDTF Client bridges the gap between the raw token tree stored into your repository - in Specify database - and the live token tree instance provided by the SDK.

hashtag
Mutate the token tree locally

Since the client is instantiated locally, all changes made to the token tree are only affecting the current version of the client.

You can create several instances of the same initial token tree - using

Then, create several updates:

  • with predefined formatters using

  • with cherry-picking of a sub-tree using

  • with custom implementation using

hashtag
Convert tokens to XXX

Converting tokens is always a matter of iterating over the tokens of your tree and decide what to do with their value(s), their and potential .

circle-info

Looking for usage? 👉 heads up to the guide.

Getting started

Getting started with the Specify CLI

hashtag
Automatic Installation

If you are on OS X or Linux, you can use the automated downloader, which will fetch the latest release version for you and install it:

To install a specific version, use the flag VERSION:

Specify SDK usage 101

This page is the starting point of multiple guides that will help you to learn how to use our SDK. If you still don't know what our SDK is, you can have a look right .

If you're not sure if you should use the or the , you can have a look to this .

Otherwise, if you're here to learn how to use the SDK, you're at the right place!

Parsers Engine
destinations
Parsers
SDTF Engine
SDK
CLI
GitHub
Destination templates
Select "GitHub application"
  • Connect or select your GitHub account

  • Select the GitHub repository you want to distribute your design data to

  • Name for your configuration file (Learn More ↗︎arrow-up-right)

  • Create the Pull Request containing your configuration file

  • Merge the PR created by Specify containing your configuration file

  • Specify will now automatically sync design data to your GitHub repository 🎉

  • How to sync design tokens in a GitHub monorepoarrow-up-right
    How to run Style Dictionary with a GitHub Actionarrow-up-right
    and many more
    Specify SDK usage 101
    SDTF
    organization
    Specify Client API
    repository
    SDTF Client API
    SDTF Engine
    clone()
    update()
    pick()
    forEachTokenState()
    modes
    aliases
    Convert a token to XXX
    here
    Specify CLI
    Specify SDK
    comparison

    Specify CLI usage 101

    This page is the starting point of multiple guides that will help you to learn how to use our CLI. If you still don't know what our CLI is, you can have a look right here.

    If you're not sure if you should use the Specify CLI or the Specify SDK, you can have a look to this comparison.

    Otherwise, if you're here to learn how to use the CLI, you're at the right place!

    Converters

    Converters are the way of converting a token to a specific syntax in the Specify SDK.

    Here are the available syntaxes:

    • CSS

    Stateful Value

    circle-exclamation

    This page is work in progress 🚧

    Specify SDK
    and many more
    and many more
    SDTF Query Language
    Specify SDK
    parsers
    hashtag
    Installation via JS ecosystem
    npm install -D @specifyapp/cli
    yarn install -D @specifyapp/cli
    pnpm install @specifyapp/cli -D
    bun add @specifyapp/cli --dev

    hashtag
    Installation via Homebrew

    Coming soon

    hashtag
    Installation via Scoop

    Coming soon

    const query: SDTFQuery = {
      where: {
        collection: '^MyCollection$',
        andWhere: {
          group: '^MyGroup$',
          andWhere: {
            token: '.*',
            select: true,
          },
        },
      },
    };
    curl -sL https://static.specifyapp.com/cli/install.sh | sh
    curl -sL https://static.specifyapp.com/cli/install.sh | VERSION="x.y.z" sh
    Available sources
    Learn more ↗
    templates ↗️
    Specify account settingsarrow-up-right
    to-css-custom-properties

    Glossary

    Looking for a definition? We've got your back!

    hashtag
    Organization (workspace)

    An organization, sometimes called workspace, is the Specify place you share with your team.

    hashtag
    Repository

    A repository is like a directory containing your design tokens and assets. The design data is stored within a JSON compatible tree built upon the .

    hashtag
    Source

    A source belongs to a repository, it represents where the original design tokens and/or assets come from - think of (Variables or Local Styles) or .

    hashtag
    Destination

    A destination belongs to a repository, it represents where the formatted design data get distributed on updates - think of or any automation you can come up with our and .

    hashtag
    Specify Design Token Format (SDTF)

    The SDTF stands for . It's a token format that helps you sync design tokens from Figma Styles, Figma Variables, and Tokens Studio. Your design tokens are created and saved as a token tree you can query and transform thanks to the Specify API. The SDTF is composed of more than 50 token types. In a SDTF token tree, design tokens can be organized in Collections, Groups, and Modes.

    hashtag
    Token type

    A Token type is a type of design token or asset supported by Specify like a color, a textStyle or a vector.

    hashtag
    Collection

    A collection is a way to group design tokens while ensuring the modes of the tokens they contain within the SDTF. A Collection can contain groups, and design tokens with one or several modes.

    hashtag
    Group

    A group is way to semantically group design tokens in the SDTF. A group contains design tokens, other groups and exists in the context of a collection.

    hashtag
    Alias

    An alias design token is a design token whose value is not a raw value but a reference to another design token's value.

    hashtag
    Mode

    A Mode is a way to associate the value of a design token to a specific context. For instance, a color named background can have several values associated to different modes like Light or Dark.

    hashtag
    Parsers Engine

    The is the high-level design data transformation and export API.

    hashtag
    Parser Rule

    A is a JSON object describing a series of parsers and options, responsible to transform and export your design tokens and assets to your code base.

    hashtag
    Parser

    are functions allowing you to transform design tokens and assets coming from Specify to fit your needs and company standards. .

    hashtag
    SDTF Client

    The offers versatile methods for working with token trees, enabling data manipulation, transformation, and conversion into various formats or languages, bridging the gap between stored and live token instances within your repository.

    hashtag
    SDTF Engine

    The brings together the core APIs that enable most of Specify features to seamlessly integrate, whether you're working in Figma or Token Studio, managing GitHub pull-requests, or directly within your codebase with the SDK or the CLI.

    hashtag
    Configuration file

    A file is a JSON or JavaScript file, made of one or many rules and the credentials to authenticate. The configuration is used to communicate with the Parsers Engine.

    hashtag

    Overview

    Take some time to understand the basic concepts of Specify. Getting to know its foundational ideas will help you glimpse into how to leverage the apps and APIs in your projects.

    The Specify platform is based on the following pillars.

    • Organization

    • Repository

    Above them, sit the engines:

    • The , accessible via all , offers an high level abstraction to directly transform any SDTF token tree into many well-known technologies like CSS, tailwind, style-dictionary…

    • The , accessible via the , offers a fine grained control over your SDTF token tree by allowing any mutation and post-process hooks, directly in your codebase.

    hashtag
    Organization

    As a Specify user, you belong to an organization.

    In this organization, you create repositories to structure your projects.

    circle-info

    All over Specify APIs, you access your organization using its namespace (e.g @specifyapp)

    hashtag
    Repository

    The repository is where the design data get stored, merged and eventually deleted over iterations.

    A repository is exactly one design token tree built with the .

    circle-info

    You can explore a repository design token tree by heading to the Specify .

    hashtag
    Source

    A source belongs to a , it represents where the original design data come from - think of (Variables or Local Styles) or .

    You can add many sources into a single repository. Read more about the available compatible with Specify.

    hashtag
    Collecting the design data

    Once a source is configured, Specify starts to collect your design data. Over this process, the acquires, validates and converts the original data into a SDTF token tree that get stored within the repository.

    From this point on, you can synchronize your source(s) with Specify anytime, at a click of a button.

    circle-info

    You can review your sources status by heading to any repository page on the Specify .

    hashtag
    Repository with many sources

    Data from many sources get automatically merged into a consolidated SDTF token tree. If Specify detects a conflict over sources, it will refuse to synchronize the first source that cause the conflict and turn it into an error state.

    hashtag
    Destination

    A destination belongs to a , it represents where the formatted design data get distributed on updates - think of or any automation you can come up with our and .

    You can add many destinations into a single repository. Read more about the available compatible with Specify.

    hashtag
    Distributing the design data

    While the source part is highly automated and managed by Specify internals, the destination offers many levers of control over the transformation required to meet your company standards.

    Two main APIs at stake here:

    • The , accessible via all , offers an high level abstraction to directly transform any SDTF token tree into many well-known technologies like CSS, tailwind, style-dictionary…

    • The , accessible via the , offers a fine grained control over your SDTF token tree by allowing any mutation and post-process hooks, directly in your codebase.

    Figma Variables & Styles

    In this guide, you’ll learn how to sync your Figma Variables and/or Styles to a Specify Repository and how to keep them updated.

    hashtag
    Before getting started

    To get the most out of this guide, you will need:

    • A Specify Account

    • A Figma file containing Variables and/or Styles

    hashtag
    1. Sync your Figma Variables and Styles with a Specify Repository

    • Access your Figma file which includes the Variables and/or Styles you would like to sync to Specify

    • the Specify Widget in the Figma file which includes your Variables and/or Styles. Or update the widget if you already have it. To update the Specify Widget you have to disconnect it, remove the entire frame in all of your Figma files, and reopen the latest version of the Widget.

    hashtag
    2. Connect your Specify account

    1. Follow the steps in the widget to connect your account. You will need to create a personal access token and you will need to add the link to the Figma file to which the widget is added.

    2. Click "Connect"

    3. Choose Advanced Repository

    circle-info

    Note: the widget can be used for syncing Figma Styles from the Classic repositories as well, you can switch between the two by clicking "Switch to".

    hashtag
    3. Create a repository in Specify

    1. Go to your Specify workspace

    2. Click on "Create repository"

    3. Choose a name

    hashtag
    4. Connect the Specify Repository in your Widget

    1. Go back to your Figma file that includes the Variables and/or Styles

    2. Click on "Create Source" in the Specify Widget

    3. It will show you the local collections and styles that are detected

    circle-info

    Make sure to understand that only Advanced repositories are listed here.

    hashtag
    5. Sync updates variables and styles on the fly

    Use the Sync button to update your Variables and/or Styles with Specify. Now you are ready to ! 🎉

    hashtag
    6. Check or delete your source inside the Specify interface

    • You will see the category of design tokens that are synced on the left-hand side

    • In the Sources section (left-hand menu), you will see your connection(s) and when the last sync between your Figma file and Specify repository has occurred.

    • Delete your source by clicking on the 3 dots option menu in the source card.

    Cover
    Cover

    Parsers Engine

    Configure automated pipelines and deliver the design data to battle-tested technologies like CSS, Style Dictionary, Javascript, TypeScript, Swift, Kotlin — and more.

    hashtag
    Overview

    The Parsers Engine is a set of APIs you interact with anytime you need to transform your design data - tokens & assets - from your repository token tree into pre-configured file outputs.

    The Parsers Engine serves as the orchestrator for a series of functions, called designed to transform design data, in parallel and/or chained. By configuring , you customize how and where design tokens and assets are exported.

    Generate Files

    The Specify CLI can be configured by default with a configuration file named .specifyrc.json . You can also override these settings .

    The configuration file lets you describe your transformation pipelines (aka ) following the to generate your output. The guide provides full documentation of the configuration file API.

    circle-info

    Specify CLI

    Here you can find all the detailed informations about the CLI

    hashtag
    Global flags

    hashtag
    -h, --help

    SDTF Engine

    The SDTF Engine API reference.

    hashtag
    Instances

    The SDTF Engine can be accessed in:

    • the @specifyapp/sdk package, by using the specifyClient.getRepositoryTokenTree

    Specify SDK

    Specify SDK API Reference

    hashtag
    Overview

    The Specify SDK host several APIs that unwraps as follow

    • SpecifyClient

    curl -sL https://static.specifyapp.com/cli/install.sh | sh
    module.exports = {
      version: "2",
      repository: '@workspace/repository',
      personalAccessToken: '<your-personal-access-token>',
      rules: [],
    };
    .specifyrc.json
    {
      "version": "2",
      "repository": "@workspace/repository",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": []
    }
    .specifyrc.js
    module.exports = {
      version: "2",
      repository: '@workspace/repository',
      personalAccessToken: '<your-personal-access-token>',
      rules: [],
    };
    .specifyrc.json
    {
      "version": "2",
      "repository": "@workspace/repository",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": []
    }
    .specifyrc.js
    module.exports = {
      version: "2",
      repository: '@workspace/repository',
      personalAccessToken: '<your-personal-access-token>',
      rules: [
        {
          name: "Generate tokens as CSS Custom Properties",
          parsers: [
            {
              name: "to-css-custom-properties",
              output: {
                type: "file",
                filePath: "tokens.css"
              }
            }
          ]
        }
      ]
    };
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Generate tokens as CSS Custom Properties",
          "parsers": [
            {
              "name": "to-css-custom-properties",
              "output": {
                "type": "file",
                "filePath": "tokens.css"
              }
            }
          ]
        }
      ]
    }
    specify pull
    Show the help

    hashtag
    -v, --version

    Show the current version of the CLIx

    hashtag
    -C, --config-path [configPath]

    The path to your JSON or Javascript config file, or the folder containing all your configs (default: .specifyrc.json)

    hashtag
    -p, --personal-access-token <personalAccessToken>

    Your personal access token. . It will try to read it from the env var SPECIFY_PAT as well.

    circle-info

    Need a personal access token? Generate one ↗arrow-up-right

    hashtag
    -r, --repository <repository>

    The repository containing the design tokens you're requesting. It follows the pattern @organizationName/repositoryName. It will try to read it from the env var SPECIFY_REPOSITORY as well.

    hashtag
    Init

    Generate a Specify ready-to-use configuration to pull your design data from your Specify repository.

    circle-exclamation

    For now it will only generate an empty config file, but we will bring back the template selection soon

    hashtag
    Flags

    hashtag
    --root [.]

    By default everything is generated in the current dir, but you can choose a different path to generate your configuration file (Default: .)

    hashtag
    Pull

    Pull the tokens from the repository following the configs

    hashtag
    Flags

    No flags for now

    Specify Design Token Format
    Figma
    Tokens Studio
    GitHub
    CLI
    SDK
    Specify Design Token Format
    Parsers Engine
    parser rule
    Parsers
    See all parsers
    SDTF Client
    SDTF
    SDTF Engine
    Specify configuration
    Source
    Destination
    Parsers Engine
    destinations
    SDTF Client
    SDK
    Specify Design Token Format
    webapparrow-up-right
    Repository
    Figma
    Tokens Studio
    Sources
    SDTF Engine
    webapparrow-up-right
    Repository
    GitHub
    CLI
    SDK
    Destinations
    Parsers Engine
    destinations
    SDTF Client
    SDK
    Cover
    Cover
    Cover
    Cover
    Cover
    Cover
    The Parsers Engine can operate both remotely, using the Pipe Engine API over HTTP, and locally, through the Specify SDK. This dual capability enables it to be integrated into various workflows, offering flexibility whether working from remote servers, custom CI or directly within local development environments.

    The Parsers Engine relies on the Parsers functions, which are actual converter implementations from the SDTF tokens to their representation in various formats and languages.

    hashtag
    Parser Rules

    As the medium to interact with the Parsers Engine, the parser rules are meant to be configured to follow your output needs.

    A parser rule is simply a JSON object with two properties:

    name: the name of the rule — we recommend setting up the purpose: CSS variables, Icons export...

    parsers: an array of parser configuration

    hashtag
    Parser Configuration

    name: the name of the parser - to-json, to-tailwind over the parsers list.

    output: an object describing the desired output type - can be optional when no output is expected.

    circle-info

    Looking for how to configure the output property? 👉 review the Rule Output type reference.

    options: an object of arbitrary keys and values defined by the parser - can be optional.

    circle-info

    Looking for a precise parser option? 👉 review the full parsers list.

    hashtag
    Example of a parser rule for a CSS custom properties output

    hashtag
    Parser rules templates

    circle-info

    To get started with the parser rules 👉 heads up to the Parser Rules templates resources.

    hashtag
    Remote execution

    Executed over HTTP, the parser rules are sent over to Specify servers to evaluate, transform and return the structured result.

    All destinations are compatible with the remote execution.

    For some destinations, like Github & the Specify CLI you need a Configuration file in order to keep track of your desired settings.

    hashtag
    Configuration file

    A Specify configuration file allows you to:

    • Define the repository source from which to fetch design data.

    • Provide a personal access token as authentication credentials for your user.

    • Describe the parsers rule(s) to configure your ouput.

    By default the file is named .specifyrc.js or .specifyrc.json

    circle-info

    Looking for the configuration file details? 👉 review the Configuration file reference

    hashtag
    Local execution

    Executed on the local environment, the parser rules are evaluated by the Specify SDK.

    Only the generation parsers are meant to be used in a local execution. In fact, you can apply any mutation on your token tree using the SDTFClient API, before generating the final output.

    Once executed, the parsers pipeline yields the result back to the SDK instance.

    circle-info

    Get started with local execution 👉 review the Execute a parser locally guide.

    hashtag
    The Parsers functions

    Parsers play a pivotal role. They are functions acting as interpreters and transformers of design tokens. They process the initial token tree and apply a set of transformations toward the expected output. Each parser, whether it pertains to utilities or generation, contributes to the workflow by either mutating the token tree or generating tangible assets and code snippets respectively.

    circle-info

    Looking for the parsers settings? 👉 review the Parsers reference.

    hashtag
    Utility parsers

    Utility parsers mutate the design token tree, through dedicated functions like filter, convert-color, change-case…

    You configure their options property to instruct the changes to apply to your token tree before it get passed to the next step.

    circle-info

    The utility parsers are only available to the Remote execution of the Parsers Engine.

    hashtag
    Generation parsers

    Code generation parsers generate one or several outputs like CSS, JavaScript, TypeScript or assets like SVGs or bitmaps.

    You configure their output property to produce the files that will end up in your codebase.

    The options property is also available for most of the generation parsers.

    Parsers
    Parser Rules
    hashtag
    Example: Export All Tokens and Assets In JSON
    from command line parameters
    Parser Rule
    Parser Engine API
    configuration file 101
    Here is the complete list of available flags for the Specify CLI
    SPECIFY_PAT=xxxxxxxxxxxxxxxxxx specify pull \
        -C "my-custom-config.json" \
        -r "<@your-organization/your-repository-name>"
    method.
  • the @specifyapp/specify-design-token-format package, by using the exported createSDTFEngine function.

  • hashtag
    Public API

    hashtag
    Query methods

    The Query API provides methods for locally accessing and iterating the token tree.

    hashtag
    Query runner

    The SDTF Query Language is used into the sdtfEngine.query.run(query: SDTFQuery)

    hashtag
    Mutation methods

    The Mutation API provides methods for locally mutating the token tree.

    hashtag
    Top level

    hashtag
    renderJSONTree

    Get the JSON representation of the token tree.

    {
      "rules": [
        {
          "name": "Generate tokens as CSS Custom Properties",
          "parsers": [
            {
              "name": "to-css-custom-properties", // parser name
              "output": {
                "type": "file",
                "filePath": "public/css/tokens.css" // ouput will be a file
              },
              "options": { // parser specific options
                "tokenNameTemplate": "--{{groups}}-{{token}}",
                "selectorTemplate": "[data-theme=\"{{mode}}\"]"
              }
            }
          ]
        }
      ]
    }
    my-custom-config.json
    {
      "version": "2",
      "rules": [
        {
          "name": "Export All Tokens and Assets In a JSON File In SDTF",
          "parsers": [
            {
              "name": "to-sdtf",
              "output": {
                "type": "file",
                "filePath": "sdtf.json"
              }
            }
          ]
        }
      ]
    }
    sdtfEngine.[method]
    function renderJSONTree(param: {
      renderOptions:
        | {
            resolveAliases: true;
            allowUnresolvable?: AllowUnresolvable;
            targetMode?: TargetMode;
          }
        | { resolveAliases: false };
    }): JSON;
    Select "Advanced Repository" (Learn more ↗︎arrow-up-right)
  • Click "Create repository"

  • Select the design tokens format you want to collect from your Figma file. Learn More ↗︎arrow-up-right
  • Select the Specify repository you want to sync with. You should be able to see the Repository you have just created in Specify (if not, reload the list).

  • Click "Save to Specify"

  • You will immediately see the repository listed with the latest syncing time

  • Download ↗arrow-up-right
    Watch tutorial ↗arrow-up-right
    export your design tokens
    Create a repository in Specify
    Create a source in your Specify Widget in your Figma file
    Use the sync button to update new variables

    SDTFClient

    • SDTFEngine

      • TokenState | CollectionState | GroupState

      • Query & Mutation

      • Query Runner

    hashtag
    Usage

    Follow the Getting started guide.

    hashtag
    Package

    The @specifyapp/sdk TypeScript package is available on npm.

    hashtag

    HTTP API

    Specify HTTP API reference

    hashtag
    Overview

    The Specify HTTP API is accessible over the following base url:

    circle-info

    The Specify HTTP API is currently read-only upon your design data.

    hashtag
    Authentication

    In order to request the routes of the HTTP API, you need to set a Personal Access Token within your request headers like so:

    circle-info

    Get a Personal Access Token from

    hashtag
    Headers

    To work with the Specify HTTP API, we recommend the use of the following headers:

    hashtag
    Endpoints

    Verb
    Route
    Description

    Authentication

    Specify use a Personal Access Token for the authentication. You can create it from the dedicated page on the web apparrow-up-right. Once you have a personal access token, you can use it in two ways:

    hashtag
    Environment variable (recommended)

    You can use the environment variable named SPECIFY_PAT

    hashtag
    Flag

    If you prefer a flag, you can use --personal-access-token or -p

    hashtag
    Configuration file (not recommended)

    You can also write the personal access token directly in the config file.

    circle-info

    The personal access token in a config file is useful if you need to execute some custom business logic. To stay safe, you should use a library like to store credentials outside of the code.

    If multiple credentials are used together, Specify will choose following this order:

    1. the flag

    2. the configuration file

    3. the environment variable

    Parser Rules templates

    Automatically distribute your design tokens and assets according to your organization's standards with our ready-to-use configuration templates.

    hashtag
    Ready-to-use configuration templates for your next project

    This section will help you get started with ready-to-use configuration template. Each configuration file will be filled by one or several configuration rules.

    circle-info

    Make sure to read the concept to better understand how configuration file works with Specify.

    Template
    Description

    SpecifyClient

    The main Specify client to interact with Specify repositories and the SDTF token tree.

    hashtag
    Properties

    hashtag
    isAuthenticated

    A boolean indicating whether the client is authenticated or not.

    hashtag
    Methods

    hashtag
    authenticate

    Authenticates the client with a Personal Access Token. Can be generated at

    hashtag
    whoAmI

    Returns the current user if authenticated.

    hashtag
    logout

    Logs out the current user.

    hashtag
    getRepositories

    Returns the repositories list of the current organization.

    hashtag
    getSDTFClientByRepositoryName

    Returns a SDTFClient instance to work with the SDTF token tree of the repository.

    hashtag
    transformTokenTreeWithRemoteParsers

    Generates Design Tokens from the given token tree and configuration parsers.

    SDTF QueryResult

    The QueryResult class API reference

    The QueryResult class provides an abstraction to work with the tree node returned by a SDTF query.

    hashtag
    Properties

    hashtag
    isContinuous

    Indicates whether the selected nodes are part of the same resulting JSON tree.

    hashtag
    Methods

    hashtag
    merge

    Produces a new tree state with the resulting nodes.

    hashtag
    hasNodeType

    Check if the result contains some nodes of the specified type.

    hashtag
    hasOnlyNodeType

    Check if the result contains only nodes of the specified type.

    hashtag
    render

    Produces an analysis of the resulting nodes.

    circle-info

    When the result is continuous, the render function returns an array of length 1

    hashtag
    getPaths

    Get the token tree path of the resulting nodes.

    hashtag
    toJSON

    Renders the resulting nodes as a JSON object using the QueryResultDetail structure.

    ParsersEngineResults

    ParsersEngineResults class API reference

    A parsersEngineResults instance is returned by the parsers engine execution.

    Example:

    hashtag
    Properties

    hashtag
    all

    This getter returns the raw results of the parser pipelines execution.

    hashtag
    hasError

    This getter returns a boolean indicating whether any of the pipelines returned an error.

    hashtag
    hasWarning

    This getter returns a boolean indicating whether any of the pipelines returned warnings.

    hashtag
    allErrorMessages

    This getter gathers the error messages from all the pipelines.

    hashtag
    allWarningMessages

    This getter gathers the warning messages from all the pipelines.

    hashtag
    allInformationMessages

    This getter gathers the information messages from all the pipelines.

    hashtag
    Methods

    hashtag
    logErrorMessages

    This method prints error messages to the console.

    hashtag
    logWarningMessages

    This method prints warning messages to the console.

    hashtag
    logInformationMessages

    This method prints information messages to the console.

    hashtag
    debug

    This method prints a summary of the execution results to the console.

    hashtag
    mapOutput

    This method maps over any output of the parsers. It is quite convenient if you want to run some post-process on the output.

    hashtag
    mapFiles

    This method maps over output files. It is quite convenient if you want to run some post-process on the files.

    hashtag
    writeToDisk

    This method writes the outputs of the parsers to the file system.

    hashtag
    serialize

    This method serializes the results before HTTP transmission.

    Playground

    Learn how to use the Specify Playground to iterate more easily on your configuration files

    hashtag
    Introduction

    The Specify Playground helps you run parsers against a token graph and see the generated output in live. It's a great tool to help you iterate on your configuration files or try new parsers on the go.

    https://iframe.specifyapp.com/sdtf-playground/index.htmlarrow-up-right

    hashtag
    Usage

    • The left side panel contains the SDTF graph you'll run your rules against. It's editable and you're free to use your own SDTF graph which you can get with the parser

    • The middle panel contains your Specify configuration rule

    • The right side panel displays the generated code

    Updating tokens

    One of the benefits to work directly with the SDK is that you can update the tokens value on the fly to match exactly your needs. Before explaining you how to do it, it's important that you note that updating tokens won't run a remote update in your repository.

    circle-info

    All the updates will be performed locally on the loaded SDTF, but that's it, there's no side effects

    hashtag

    make-line-height-relative

    This parser helps you transform your text style lineheight values relative to their font size.

    hashtag
    Interface

    hashtag
    Basic usage

    Best practices

    hashtag
    Set your Personal Access Token as an environment variable

    Your Specify personal access token must always be private. We highly recommend you to set it in a private environment variable or in a .env file.

    https://api.specifyapp.com/v2
    SPECIFY_PAT=xxxxxxxxxxxxxxxxxx specify pull
    const results: ParsersEngineResults = await SDTFClient.createParsersPipelines({ ... })()

    POST

    /parsers-engine-rpc

    Execute the Parsers Engine against a given input and Parsers Rules

    your user settings ↗︎arrow-up-right
    dotenvarrow-up-right
    {
        "repository": "<@your-organization/your-repository-name>"
        "version": "2",
        "personalAccessToken": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
        "rules": []
    }
    module.export = {
      repository: '<@your-organization/your-repository-name>'
      version: '2',
      personalAccessToken: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
      rules: []
    }
    export default {
      repository: '<@your-organization/your-repository-name>'
      version: '2',
      personalAccessToken: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
      rules: []
    }
    import type { SpecifyCLIConfigurationV2File } from '@specifyapp/sdk/bulk"'
    
    const config: SpecifyCLIConfigurationV2File = {
      repository: '<@your-organization/your-repository-name>',
      version: '2',
      personalAccessToken: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
      rules: []
    }
    
    export default config;
    specifyapp.com/user/personal-access-tokensarrow-up-right
    {
      "Text styles": {
        "Body": {
          "$type": "textStyle",
          "$value": {
            "default": {
              "font": {
                "family": "Neue Haas Grotesk Text Pro",
                "postScriptName": "Neue Haas Grotesk Text Pro",
                "weight": "roman",
                "style": "normal",
                "files": []
              },
              "fontSize": {
                "value": 14,
                "unit": "px"
              },
              "color": null,
              "fontFeatures": null,
              "lineHeight": {
                "value": 150,
                "unit": "%"
              },
              "letterSpacing": {
                "value": 0.2,
                "unit": "px"
              },
              "paragraphSpacing": {
                "value": 0,
                "unit": "px"
              },
              "textAlignHorizontal": null,
              "textAlignVertical": null,
              "textDecoration": "none",
              "textIndent": {
                "value": 0,
                "unit": "px"
              },
              "textTransform": "none"
            }
          }
        }
      }
    }
    Importing the updaters

    Before even updating the tokens, we need to import the updaters that will help us to perform the updates. Everything is grouped inside the updaters object:

    hashtag
    Update the colors

    If you need to have your colors to be converted to a specific format, you can use the prebuilt color updater:

    You can refer to the spec to see all the available colors.

    hashtag
    Update the token's name casing

    If you need to have your token's name to be formatted in a specify way: camelCase, snakeCase, etc... You can use the changeCase updater:

    You can refer to the spec to see all the available casing.

    hashtag
    Update the dimensions

    If you need to have your dimensions to be converted to a specific format, you can use the prebuilt dimension updater:

    You can refer to the spec to see all the available units.

    hashtag
    Executing multiple updates

    The update method actually takes as much parameter as you want, so you can do the following:

    hashtag
    Avoid query repetition

    If you want to perform multiple updates with the same query, it can quickly become quite verbose. To avoid it, you can use the withQuery method:

    hashtag
    Creating your custom updater

    An updater is in reality a function that looks like this:

    So if you want to create a custom updater, feel free to copy/paste the above function and implement it the way you want. You can refer to the SDTF Engine to understand how to perform updates on the SDTF.

    If you're wondering why there's 2 applyTo, it's because the first one is the one that you're passing when calling the function, and the second one is passed if the updater is called with withQuery.

    Once implemented, you can use it the same way we're using the updaters above:

    The above implementation works for a reusable updater, but if you exactly know what you want, you can do the following as well:

    Authorization: PAT <your-personal-access-token>
    Content-Type: application/json
    Authorization: PAT <your-personal-access-token>
    specify --personal-access-token xxxxxxxxxxxxxxxxxx pull
    authenticate(personalAccessToken: string): Promise<void>;
    whoAmI(): {
      email: string;
      id: string;
      username: string;
      fullname: string;
      organizations: {
        id: string;
        namespace: string;
        domain: string | null;
      }[];
    } | null;
    logout(): void;
    getRepositories(): Promise<{
      name: string;
      id: string;
      version: number;
      createdAt: string;
      updatedAt: string;
    }[]>;
    getSDTFClientByRepositoryName(repositoryName: string): Promise<SDTFClient>;
    transformTokenTreeWithRemoteParsers(
      sdtfTree: SpecifyDesignTokenFormat,
      parsers: PipeEngineParserConfiguration
    ): Promise<{
      fromRule: string;
      output: PipeEngineRuleOutput
    }>;
    get isContinuous(): boolean;
    merge(dedupeFn?: MergeDedupeFn): {
        treeState: TreeState;
    };
    hasNodeType(nodeType: 'collection' | 'group' | 'token'): boolean;
    hasOnlyNodeType(nodeType: 'collection' | 'group' | 'token'): boolean;
    type QueryResultDetail = {
      isRoot: boolean;
      parentPath: Array<string>;
      isComplete: boolean;
      nodes: Array<SDTFNodeState>;
      sdtf: SpecifyDesignTokenFormat;
    };
    render(): Array<QueryResultDetail>;
    getPaths(type: 'string'): Array<string>;
    getPaths(type: 'array'): Array<Array<string>>;
    getPaths(type?: undefined): Array<string>;
    toJSON(): {
        isContinuous: boolean;
        graphs: {
            isRoot: boolean;
            parentPath: string[];
            isComplete: boolean;
            sdtf: SpecifyDesignTokenFormat;
        }[];
    };
    get all: Array<ParsersEngineResult>;
    get hasError: boolean;
    get hasWarning: boolean;
    get allErrorMessages: ParsersEngineErrorMessage[];
    get allWarningMessages: ParsersEngineWarningMessage[];
    get allInformationMessages: ParsersEngineInformationMessage[];
    logErrorMessages(): void;
    logWarningMessages(): void;
    logInformationMessages(): void;
    debug(options?: {
        withOutputContent?: boolean;
    }): void;
    mapOutput(fn: (output: ParserOutput | null) => ParserOutput | null | void): this;
    mapFiles(fn: (file: FilesOutput['files'][number]) => FilesOutput['files'][number] | void): this;
    writeToDisk(directoryPath?: string): Promise<{
        errors: Array<any>;
        outputPaths: Array<string>;
    }>;
    serialize(): SerializedParsersEngineResults;
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Make line height relative",
          "parsers": [
            {
              "name": "make-line-height-relative",
              "options": {}
            },
            {
              "name": "to-sdtf",
              "output": {
                "type": "file",
                "filePath": "public/tokens.json"
              }
            }
          ]
        }
      ]
    }
    tokens.json
    {
      "Text styles": {
        "Body": {
          "$type": "textStyle",
          "$value": {
            "default": {
              "font": {
                "family": "Neue Haas Grotesk Text Pro",
                "postScriptName": "Neue Haas Grotesk Text Pro",
                "weight": "roman",
                "style": "normal",
                "files": []
              },
              "fontSize": {
                "value": 14,
                "unit": "px"
              },
              "color": null,
              "fontFeatures": null,
              "lineHeight": {
                "value": 1.5,
                "unit": null
              },
              "letterSpacing": {
                "value": 0.2,
                "unit": "px"
              },
              "paragraphSpacing": {
                "value": 0,
                "unit": "px"
              },
              "textAlignHorizontal": null,
              "textAlignVertical": null,
              "textDecoration": "none",
              "textIndent": {
                "value": 0,
                "unit": "px"
              },
              "textTransform": "none"
            }
          }
        }
      }
    }
    interface parser {
      name: 'make-line-height-relative';
      options: {};
    }
    import { updaters } from '@specifyapp/sdk'
    import { updaters } from '@specifyapp/sdk'
    
    sdtfClient.update(updaters.color({ toFormat: 'hex' }, { where: { token: '^color-' }}))
    import { updaters } from '@specifyapp/sdk'
    
    sdtfClient.update(updaters.changeCase({ toFormat: 'kebabCase' }, { where: { token: '.*' }}))
    import { updaters } from '@specifyapp/sdk'
    
    sdtfClient.update(updaters.dimension(
      { toFormat: 'rem', baseValue: { rem: 12 } }, 
      { where: { token: '^spacing-' }}
    )
    import { updaters } from '@specifyapp/sdk'
    
    sdtfClient.update(
      updaters.dimension(
        { toFormat: 'rem', baseValue: { rem: 12 } }, 
        { where: { token: '^spcacing-' }}
       ),
       updaters.nameCase({ toFormat: 'kebabCase' }, { where: { token: '.*' }}),
       updaters.color({ toFormat: 'hex' }, { where: { token: '^color-' }})
    )
    import { updaters } from '@specifyapp/sdk'
    
    sdtfClient
      .withQuery({ where: { token: '*' }})
      .update(
        updaters.dimension(
          { toFormat: 'rem', baseValue: { rem: 12 } }, 
        ),
        updaters.nameCase({ toFormat: 'kebabCase' }),
        updaters.color({ toFormat: 'hex' })
       )
    import type { SDTFQuery, SDTFEngine } from '@specifyapp/specify-design-token-format'
    
    function myUpdater(
      options?: {}
      applyTo?: SDTFQuery,
    ): Updater {
      return (engine: SDTFEngine, applyToInner?: SDTFQuery) => {
        const query = applyTo ?? applyToInner;
    
        // Do the conversion
      };
    }
    
    sdtfClient.update(myUpdater())
    import type { SDTFEngine } from '@specifyapp/specify-design-token-format'
    
    function myUpdater(engine: SDTFEngine) {
        // Do something
    }
    
    sdtf.update(myUpdater)

    This template helps you pull your design tokens in the SDTF format in a JSON file.

    This template helps you pull your design tokens in JSON.

    CSS Custom Properties

    This template is dedicated for Web developers using CSS. It helps you generate all types of design tokens as CSS Custom Properties in their respective CSS file.

    Tailwind

    This template is dedicated for Web developers using Tailwind theme.

    Flutter

    This template helps you generate your design tokens in a dart file and icons as SVG files.

    Parsers Engine

    hashtag
    Using an .env file in a JavaScript config file
    .specifyrc.mjs
    import path from 'node:path
    
    .specifyrc.cjs
    const path = require('
    

    hashtag
    Using the --personal-access-token CLI flag

    You can inject your personal access token through the --personal-access-token, -p CLI flag.

    circle-info

    You can use this method to sync Specify with git repositories in GitLabarrow-up-right, GitHubarrow-up-right, Azure DevOpsarrow-up-right or Bitbucketarrow-up-right.

    to-sdtf

    Specify SDK Cheatsheet

    On this page you'll find a lot of common actions you'll probably want to perform when using the SDK

    hashtag
    Creating Specify and SDTF clients

    You will first create a Specify client, then authenticate, then create a SDTF client by fetching the repository token tree.

    hashtag
    Updating tokens

    The following example is only an overview, if you need more details you can have a look .

    hashtag
    Convert a token to XXX

    The following example is only an overview, if you need more details you can have a look .

    hashtag
    Execute a parser

    The following example is only an overview, if you need more details you can have a look .

    hashtag
    Retrieving data

    hashtag
    Get a specific token

    hashtag
    Get all the tokens

    hashtag
    Map over all the tokens

    hashtag
    Get a specific group

    hashtag
    Get all the groups

    hashtag
    Map over all the groups

    hashtag
    Get a specific collection

    hashtag
    Get all collections

    hashtag
    Map over all the collections

    hashtag
    Filtering data

    hashtag
    Keeping a sub-graph from a path

    If you only want a specific portion of the graph, you can cherry-pick using the pick function:

    hashtag
    Keeping the children of a path

    If you only want the children of a specific portion of the graph you can use the pick function this way:

    hashtag
    Query a specific set of data

    You can use the query method of the SDTFClient to create a copy of the current SDTF with only the tokens that'll match your query:

    You can find all the details about querying the data .

    hashtag
    Remove tokens from the SDTF

    You can use the remove method to delete tokens based on a query:

    hashtag
    Renaming tokens

    hashtag
    Rename a node

    By not specifying any type, the method will rename any node as long as the path is valid.

    hashtag
    Rename a collection

    By specifying the type, the method will rename only a collection node.

    hashtag
    Rename a group

    By specifying the type, the method will rename only a group node.

    hashtag
    Rename a token

    By specifying the type, the method will rename only a token node.

    svg-to-tsx

    This parser helps you generate TSX components from your vector assets.

    hashtag
    Interface

    hashtag
    Options

    Parameter
    Required
    Type
    Default
    Description

    hashtag
    Basic usage

    POST /parsers-engine-rpc

    Execute the Parsers Engine against a given input and Parsers Rules.

    hashtag
    Route

    Method: POST

    Authentication: required

    Url:

    hashtag
    Headers

    hashtag
    Request Body

    Name
    Type
    Description

    hashtag
    Response Body

    circle-info

    The response is an array where each item is the result of the rule given in the request, at the same index.

    circle-info

    The output type matches the output configured within the rule given in the request.

    circle-exclamation

    The route always respond with a 200 code. Error state is represented by the status property in response object.

    hashtag
    Example

    Here's a simple example to get the raw tokens in JSON from a repository called all-design-datain the @acme-inc workspace:

    to-bitmap-file

    This parser helps you generate PNG and JPG files from your bitmap assets.

    hashtag
    Interface

    hashtag
    Options

    Parameter
    Required
    Type
    Default
    Description

    hashtag
    Basic usage - single mode

    This will generate a single bitmap file like so:

    • public/assets/image.png

    hashtag
    Advanced usage - Several modes by definition (@1x, @2x...)

    Our bitmap is named "image", is in a group named "assets", and has 2 modes for each definition parameter: 1x and 2x.

    By default, this parser will generate the following output: {directoryPath}/{groups}/{bitmapName}.{png|jpg}

    This will generate the following bitmap files:

    • public/assets/image-1x.png

    • public/assets/image-2x.png

    to-file

    This parser helps you generate files from any asset token: Bitmap, Font and Vector.

    hashtag
    Interface

    hashtag
    Options

    Parameter
    Required
    Type
    Default
    Description

    hashtag
    Basic usage

    This will generate the corresponding font file binary, named after the filenameTemplate.

    • public/inter-regular.ttf

    JSON

    This template helps you pull your design tokens in JSON.

    This example uses the following parser:

    • to-json to generate your design tokens in JSON

    If you use the CLI, you need to fill three properties:

    • repository is @organization/repository

    • personalAccessToken which you can generate

    • rules are where you provide parsers and compatible options

    If you use the GitHub, you need to fill 4 properties:

    • repository is @organization/repository

    • head lets you set the branch your PR will be created on

    SDTF

    This template helps you pull your design tokens in the SDTF format in a JSON file.

    This example uses the following parser:

    • to-sdtf to generate your design tokens in SDTF

    If you use the CLI, you need to fill three properties:

    • repository is @organization/repository

    • personalAccessToken which you can generate

    • rules are where you provide parsers and compatible options

    If you use the GitHub, you need to fill 4 properties:

    • repository is @organization/repository

    • head lets you set the branch your PR will be created on

    select-modes

    This parser helps you select design tokens from specific mode(s).

    hashtag
    Interface

    hashtag
    Options

    svgo

    This parser help you optimize and transform your SVG strings with svgo.

    hashtag
    Interface

    hashtag
    Options

    SDTFClient

    The client to use SDTFEngine and Parsers Pipeline APIs. It provides methods for interacting with the token tree of a repository.

    hashtag
    Properties

    hashtag
    engine

    Query API

    Methods for locally accessing and iterating the token tree.

    You access each of the query methods over:

    hashtag
    getTokenState

    Get a tokenState instance from the tokens token tree.

    Flutter

    This template helps you generate your design tokens in a dart file and icons as SVG files.

    This example uses the following parsers:

    • to generate your design tokens as dart files

    • to optimize and generate icons as SVG files

    .env
    SPECIFY_ACCESS_TOKEN=ab83f8f49f5c65456c7b1fe70efbc804aa08f87150214aa984d4125945ed8283bash
    specify pull -p $SPECIFY_TOKEN
    import { createSpecifyClient } from "@specifyapp/sdk";
    
    const specifyClient = createSpecifyClient();
    await specifyClient.authenticate("<YOUR_PERSONAL_ACCESS_TOKEN_VAR>");
    
    const sdtfClient = 
      await specifyClient.getSDTFClientByRepositoryName("<YOUR_SPECIFY_REPO_NAME>");
      
      console.log('Repository name',sdtfClient.repository.name)
    interface parser {
      name: 'svg-to-tsx';
      output: {
        type: 'directory';
        directoryPath: string;
      };
      options?:{
        reactVersion?: string; // default to 17.0.0
        filePrefix?: string;
        fileSuffix?: string;
        exportDefault?: boolean;
        tokenNameTemplate?: string;
      }
    }
    interface parser {
      name: 'to-bitmap-file';
      options: {
        filenameTemplate: string;
      };
      output: {
        type: 'directory';
        directoryPath: string;
      };
    };
    interface parser {
      name: 'to-file';
      options: {
        filenameTemplate: string;
      };
      output: {
        type: 'directory';
        directoryPath: string;
      };
    };
    '
    ;
    import process from 'node:process';
    import dotenv from 'dotenv';
    dotenv.config({
    path: path.resolve(process.cwd(), '.env.specify-cli'),
    });
    export default {
    version: '2',
    repository: '@workspace/repository',
    personalAccessToken: process.env.SPECIFY_ACCESS_TOKEN,
    rules: [],
    };
    path
    '
    )
    ;
    const envFile = '.env';
    require('dotenv').config({ path: path.resolve(process.cwd(), envFile) });
    module.exports = {
    version: '2',
    repository: '@workspace/repository',
    personalAccessToken: process.env.SPECIFY_TOKEN,
    rules: [],
    };
    SDTF
    JSON
    Learn more about Specifyarrow-up-right
    How to sync Figma Variables and Styles to Specify (3min)
    https://www.npmjs.com/package/@specifyapp/sdkwww.npmjs.comchevron-right
    here
    here
    here
    here

    false

    string

    ''

    Add an arbitrary content at the beginning of the generated file.

    fileSuffix

    false

    string

    ''

    Add an arbitrary content at the end of the generated file.

    exportDefault

    false

    boolean

    true

    Whether use named or default export.

    tokenNameTemplate

    false

    string

    '{{token}}'

    Change the template of the generated named export.

    reactVersion

    false

    string

    The React version you're using. Depending on your version, this parser will automatically import React in your JSX component.

    filePrefix

    Record<string, boolean | undefined> | undefined

    Select which response keys should be present. Defaults to true.

    dataBox

    ParsersEngineDataBox

    The initial state to launch the parsers engine from.

    rules

    Array<ParserRule>

    The parser rule definitions to instruct the transformation and generation pipelines.

    returnedKeys

    filenameTemplate

    false

    string

    "{{parents}}/{{name}}[-{{mode}}]{{extension}}"

    The mustache template used to generate the file path to write to the file system. Available variables: parents: group and collection names of the token's parents name: the name of the asset token mode: the name of the current mode extension: the file extension extracted from the token

    {
      "assets": {
        "image": {
          "$type": "bitmap",
          "$value": {
            "1x": {
              "url": "<url-of-your-bitmap-file>",
              "format": "png",
              "width": 623,
              "height": 415,
              "variationLabel": null
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Generate images",
          "parsers": [
            {
              "name": "to-bitmap-file",
              "output": {
                "type": "directory",
                "directoryPath": "public"
              }
            }
          ]
        }
      ]
    }

    filenameTemplate

    false

    string

    "{{parents}}/{{name}}[-{{mode}}]{{extension}}"

    The mustache template used to generate the file path to write to the file system. Available variables: parents: group and collection names of the token's parents name: the name of the asset token mode: the name of the current mode extension: the file extension extracted from the token

    {
      "fonts": {
        "inter-regular": {
          "$type": "font",
          "$value": {
            "default": {
              "family": "Inter Regular",
              "postScriptName": "Inter Regular",
              "weight": "regular",
              "style": "normal",
              "files": [
                {
                  "format": "ttf",
                  "url": "https://static.specifyapp.com/sdtf-seeds/inter-regular.ttf",
                  "provider": "Specify"
                }
              ]
            }
          }
        }
      }
    }

    base lets you set the branch your PR will be merged on

  • rules lets you transform tokens by chaining parsers

  • circle-info

    Make sure you have connected your GitHub account with your Specify account. Head toward this articlearrow-up-right to learn more.

    in your account settingsarrow-up-right
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Generate tokens in JSON",
          "parsers": [
            {
              "name": "to-json",
              "output": {
                "type": "file",
                "filePath": "output/tokens.json"
              }
            }
          ]
        }
      ]
    }

    base lets you set the branch your PR will be merged on

  • rules lets you transform tokens by chaining parsers

  • circle-info

    Make sure you have connected your GitHub account with your Specify account. Head toward this articlearrow-up-right to learn more.

    in your account settingsarrow-up-right
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Generate tokens in SDTF",
          "parsers": [
            {
              "name": "to-sdtf",
              "output": {
                "type": "file",
                "filePath": "output/tokens.json"
              }
            }
          ]
        }
      ]
    }
    This property represents the SDTF engine used by the SDTFClient.

    hashtag
    repository

    This property represents the repository that the SDTFClient is interacting with.

    hashtag
    Methods

    hashtag
    getJSONTokenTree

    Returns the JSON token tree from the current repository.

    hashtag
    clone

    Create a new SDTFClient instance to avoid mutating the current token tree. Especially useful when you want to perform multiple distinct operations on the same token tree.

    hashtag
    pick

    Narrow the current token tree by picking a subtree based on the given path.

    hashtag
    query

    Create a clone of the current SDTF and only keep the selection of the query. When creating a new tree it's possible that tokens, groups and collections names will collide.

    hashtag
    renameNode

    Rename a node to a new name. If you specify a type, it'll rename the node only if it's matching the type parameter. If it doesn't, it'll throw an error.

    hashtag
    Example

    hashtag
    update

    Execute an update on the current token tree. Note that the update won't be applied to the remote repository.

    hashtag
    withQuery

    Execute multiple updater functions with the same query.

    hashtag
    resolveAliases

    Resolve aliases in the current token tree. Useful before pick to avoid unresolvable aliases.

    hashtag
    remove

    Narrow the current token tree by removing any matching node based on the given query.

    hashtag
    reset

    Resets the current token tree to its initial value. The initial value being the token tree of the repository at the time of the creation of the first SDTFClient instance.

    hashtag
    executeEngine

    Tap into the current token tree to perform custom side effects.

    hashtag
    forEachTokenState

    Iterate against the tokenStates of the current token tree.

    hashtag
    mapTokenStates

    Iterate against the tokenStates of the current token tree, and accumulate the results in an array.

    hashtag
    getTokenState

    Get a token state for a given path.

    hashtag
    getAllTokenStates

    Get all the token states.

    hashtag
    forEachCollectionState

    Iterate against the collectionStates of the current token tree.

    hashtag
    mapCollectionStates

    Iterate against the collectionStates of the current token tree, and accumulate the results in an array.

    hashtag
    getCollectionState

    Get a collection state for a given path.

    hashtag
    getAllCollectionStates

    Get all the collection states.

    hashtag
    forEachGroupState

    Iterate against the groupStates of the current token tree.

    hashtag
    mapGroupStates

    Iterate against the groupStates of the current token tree, and accumulate the results in an array.

    hashtag
    getGroupState

    Get a group state for a given path.

    hashtag
    getAllGroupStates

    Get all the group states.

    hashtag
    forEachQueryResult

    Iterate against the nodeStates given by the query.

    hashtag
    mapQueryResults

    Iterate against the nodeStates given by the query, and accumulate the result in an array.

    hashtag
    createParsersPipelines

    Create a parsers engine executor from the custom or built-in parser functions passed as arguments. All pipelines are executed in parallel, if you need to chain parsers, have a look to the chainParserFunctions util.

    hashtag
    createParsersPipelinesFromRules

    Create a parsers engine executor from the built-in parser rules passed as arguments. All pipelines are executed in parallel, if you need to chain parsers, have a look to the chainParserFunctions util.

    hashtag
    getGroupState

    Get a groupState instance from the tokens token tree.

    hashtag
    getCollectionState

    Get a collectionState instance from the tokens token tree.

    hashtag
    getNearestCollectionState

    Get the collectionState instance enclosing the given path.

    hashtag
    getAllTokenStates

    Get all tokenState instances from the tokens token tree.

    hashtag
    getAllGroupStates

    Get all groupState instances from the tokens token tree.

    hashtag
    getAllCollectionStates

    Get all collectionState instances from the tokens token tree.

    hashtag
    getAllNodeStates

    Get all tokenState, groupState and collectionState instances from the tokens token tree.

    hashtag
    getTokenChildrenOf

    Get the tokenState instances that are children of the given path.

    hashtag
    getGroupChildrenOf

    Get the groupState instances that are children of the given path.

    hashtag
    getCollectionChildrenOf

    Get the collectionState instances that are children of the given path.

    hashtag
    getChildrenOf

    Get the tokenState, groupState and collectionState instances that are children of the given path.

    hashtag
    getParentsOf

    Get the groupState and collectionState instances that are parents of the given path.

    hashtag
    getGroupChildren

    Get groupState instances that are direct children of the given path.

    hashtag
    getTokenChildren

    Get tokenState instances that are direct children of the given path.

    hashtag
    getCollectionChildren

    Get collectionState instances that are direct children of the given path.

    hashtag
    renderJSONTree

    Get the JSON representation of the tokens token tree.

    hashtag
    getAliasReference

    Get the aliasReference instance from given coordinates.

    hashtag
    getAllAliasReferences

    Get all aliasReference instances from the tokens token tree.

    hashtag
    getAliasReferencesTo

    Get all aliasReference instances that reference the given "to" coordinates.

    hashtag
    getAliasReferencesFrom

    Get all aliasReference instances that reference the given "from" coordinates.

    hashtag
    getStatefulAliasReference

    Get the statefulAliasReference instance of the given "from" coordinates.

    hashtag
    getStatefulAliasReferencesTo

    Get the statefulAliasReference instances that reference the given "to" coordinates.

    hashtag
    getStatefulAliasReferencesFrom

    Get the statefulAliasReference instances that reference the given "from" coordinates.

    import { updaters } from '@specifyapp/sdk'
    
    sdtfClient.update(
      updaters.color({ toFormat: 'hex' }, { where: { token: '^color-' }}),
    );
    import {
      dimensionToCss,
      breakpointToCss,
      colorToCss,
      createResolveAliasStrategy 
    } from '@specify/sdk/css'
    
    const strategy = createResolveAliasStrategy();
    
    const outputs = sdtfClient.mapTokenStates(tokenState => 
      tokenState.matchByType(
        {
          dimension: dimensionToCss(strategy) // Output example: '12px'
          breakpoint: breakpointToCss(strategy) // Output example: '12px'
          color: colorToCss(strategy) // Output example: '#ff00ff'
        }, 
        _ => undefined
      )
    );
    import { parsers } from '@specify/sdk'
    
    const executePipelines = sdtfClient
      .createParsersPipelines(
        parsers.toCssCustomProperties({ filePath: 'myFile.css' }),
      );
    const parsersEngineResults = await executePipelines();
    const tokenState = sdtfClient.getTokenState(['path', 'to', 'token']);
    const tokenStates = sdtfClient.getAllTokenStates();
    const results = sdtfClient.mapTokenStates(tokenState => ...);
    const group = sdtfClient.getGroupState(['path', 'to', 'myGroup']);
    const groups = sdtfClient.getAllGroupStates();
    const results = sdtfClient.mapGroupStates(tokenState => ...);
    const collection = sdtfClient.getCollectionState(['path', 'to', 'myCollection']);
    const collections = sdtfClient.getAllCollectionStates();
    const results = sdtfClient.mapCollectionStates(tokenState => ...);
    const colors = sdtfClient.pick(['colors']);
    const colors = sdtfClient.pick(['colors', '*']);
    const colorAndTextStyleOnlySdtf = sdtfClient.query({
      where: {
        token: '.*',
        withTypes: {
          include: ['color', 'textStyle']
        }
      }
    });
    sdtfClient
      .remove({ where: { token: '^Blue$', select: true } })
    sdtfClient.renameNode({ atPath: ['my', 'path' ], name: 'newName' });
    sdtfClient.renameNode({ 
      atPath: ['my', 'path' ],
      name: 'newName',
      type: 'collection'
    });
    sdtf.renameNode({ 
      atPath: ['my', 'path' ],
      name: 'newName',
      type: 'group'
    });
    sdtf.renameNode({ 
      atPath: ['my', 'path' ],
      name: 'newName',
      type: 'token'
    });
    17.0.0
    {
      "icons": {
        "menu": {
          "$type": "vector",
          "$value": {
            "default": {
              "variationLabel": null,
              "format": "svg",
              "url": "<url-of-your-svg-file>"
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "name": "Generate vectors as TSX components",
      "parsers": [
        {
          "name": "svg-to-tsx",
          "output": {
            "type": "directory",
            "directoryPath": "output/assets"
          }
        }
      ]
    }
    output/assets/icons/menu.tsx
    export default () => (
        <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
            <path d="M9.25 9.25V3.5H4C3.72386 3.5 3.5 3.72386 3.5 4V9.25H9.25ZM9.25 10.75H3.5V16C3.5 16.2761 3.72386 16.5 4 16.5H9.25V10.75ZM10.75 16.5H16C16.2761 16.5 16.5 16.2761 16.5 16V10.75H10.75V16.5ZM4 18C2.89543 18 2 17.1046 2 16V4C2 2.89543 2.89543 2 4 2H10H16C17.1046 2 18 2.89543 18 4V10V16C18 17.1046 17.1046 18 16 18H4ZM16.5 9.25V4C16.5 3.72386 16.2761 3.5 16 3.5H10.75V9.25H16.5Z" fill="#788BA5" />
        </svg>
    );
    https://api.specifyapp.com/v2/parsers-engine-rpc
    Content-Type: application/json
    Authorization: PAT <your-personal-access-token>
    type ParsersEngineRPCRequestBody = {
      dataBox: ParsersEngineDataBox;
      rules: Array<ParserRule>;
      returnedKeys?: {
        output?: boolean;
        next?: boolean;
        errorMessages?: boolean;
        warningMessages?: boolean;
        informationMessages?: boolean;
      };
    };
    type ParsersEngineResults = Array<{
      pipelineName: string;
      isFromRule: boolean;
      status: "success" | "error";
      output:
        | {
            type: "files";
            files: Array<{
              path: string;
              content:
                | { type: "text"; text: string }
                | { type: "url"; url: string };
            }>;
          }
        | {
            type: "JSON";
            json: unknown;
          }
        | {
            type: "text";
            text: string;
          }
        | {
            type: "SDTF";
            graph: SpecifyDesignTokenFormat;
          }
        | null;
      next: ParsersEngineDataBox | undefined;
      errorMessages: Array<{
        type: "error";
        content: string;
        errorKey: string;
      }>;
      warningMessages: Array<{
        type: "warning";
        content: string;
        errorKey: string;
      }>;
      informationMessages: Array<{
        type: "information";
        content: string;
      }>;
    }>;
    curl -X POST 'https://api.specifyapp.com/v2/parsers-engine-rpc' \
    --header 'Authorization: PAT <YOUR-PERSONAL-ACCESS-TOKEN>' \
    --header 'Content-Type: application/json' \
    --data '{
      "dataBox": {
        "type": "repository",
        "owner": "@acme-inc",
        "name": "all-design-data"
      },
      "rules": [
        {
          "name": "HTTP Extract",
          "parsers": [
            {
              "name": "to-sdtf",
              "output": {
                "type": "file",
                "filePath": "tokens.json"
              }
            }
          ]
        }
      ]
    }'
    {
      "assets": {
        "image": {
          "$type": "bitmap",
          "$value": {
            "1x": {
              "url": "<url-of-your-bitmap-file>",
              "format": "png",
              "width": 623,
              "height": 415,
              "variationLabel": null
            },
            "2x": {
              "url": "<url-of-your-bitmap-file>",
              "format": "png",
              "width": 1246,
              "height": 830,
              "variationLabel": null
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Generate images",
          "parsers": [
            {
              "name": "to-bitmap-file",
              "output": {
                "type": "directory",
                "directoryPath": "public"
              }
            }
          ]
        }
      ]
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Generate images",
          "parsers": [
            {
              "name": "to-file",
              "options": {
                "filenameTemplate": "{{name}}{{extension}}"
              },
              "output": {
                "type": "directory",
                "directoryPath": "public"
              }
            }
          ]
        }
      ]
    }
    {
      "version": "2",
      "head": "specifyrc-json",
      "base": "main",
      "repository": "@organization/repository",
      "rules": [
        {
          "name": "Generate tokens in JSON",
          "parsers": [
            {
              "name": "to-sdtf",
              "output": {
                "type": "file",
                "filePath": "output/tokens.json"
              }
            }
          ]
        }
      ]
    }
    .specifyrc.json
    {
      "version": "2",
      "head": "specifyrc-json",
      "base": "main",
      "repository": "@organization/repository",
      "rules": [
        {
          "name": "Generate tokens in JSON",
          "parsers": [
            {
              "name": "to-sdtf",
              "output": {
                "type": "file",
                "filePath": "output/tokens.json"
              }
            }
          ]
        }
      ]
    }
    readonly engine: SDTFEngine;
    readonly repository: {
        readonly id: string;
        readonly name: string;
        readonly version: number;
        readonly createdAt: string;
        readonly updatedAt: string;
    };
    getJSONTokenTree(): SpecifyDesignTokenFormat;
    clone(): SDTFClient;
    pick(path: Array<string>): this;
    query(query: SDTFQuery, dedupeFn?: MergeDedupeFn): SDTFClient;
    renameNode(options: {
        atPath: Array<string>;
        name: string;
        type?: 'group' | 'collection' | 'token';
    }): this;
    sdtf.renameNode({ atPath: ['my', 'group'], name: 'newName' })
    update(...updaters: Array<Updater>): this;
    withQuery(query: SDTFQuery): {
        update: (...updaters: Array<Updater>) => SDTFClient;
    };
    resolveAliases(): this;
    remove(query: SDTFQuery): this;
    reset(): this;
    executeEngine(fn: (engine: SDTFEngine) => void): this;
    forEachTokenState(fn: (tokenState: TokenState, engine: SDTFEngine) => void): this;
    mapTokenStates<T>(fn: (tokenState: TokenState, engine: SDTFEngine) => T): Array<T>;
    getTokenState(path: Array<string>): TokenState | undefined;
    getAllTokenStates(): Array<TokenState>;
    forEachCollectionState(fn: (collectionState: CollectionState, engine: SDTFEngine) => void): this;
    mapCollectionStates<T>(fn: (collectionState: CollectionState, engine: SDTFEngine) => T): Array<T>;
    getCollectionState(path: Array<string>): CollectionState | undefined;
    getAllCollectionStates(): Array<CollectionState>;
    forEachGroupState(fn: (groupState: GroupState, engine: SDTFEngine) => void): this;
    mapGroupStates<T>(fn: (groupState: GroupState, engine: SDTFEngine) => T): Array<T>;
    getGroupState(path: Array<string>): GroupState | undefined;
    getAllGroupStates(): Array<GroupState>;
    forEachQueryResult(query: SDTFQuery, fn: (treeNodeState: SDTFNodeState, engine: SDTFEngine, queryResult: QueryResult) => void): this;
    mapQueryResults<T>(query: SDTFQuery, fn: (treeNodeState: SDTFNodeState, engine: SDTFEngine, queryResult: QueryResult) => T): T[];
    createParsersPipelines(
      ...parsers: Array<ParserFunction<SDTFEngineDataBox>>
    ): () => Promise<ParsersEngineResults>;
    createParsersPipelinesFromRules(
      ...parsers: Array<BuiltInGenerationParserRule>
    ): () => Promise<ParsersEngineResults>;
    sdtfEngine.query.[method]
    function getTokenState(
      path: Array<string>
    ):
      | { status: "resolved"; tokenState: TokenState<Type> }
      | { status: "unresolvable"; reason: "DOES_NOT_EXIST" };
    function getGroupState(
      path: Array<string>
    ):
      | { status: "resolved"; groupState: GroupState }
      | { status: "unresolvable"; reason: "DOES_NOT_EXIST" };
    function getCollectionState(
      path: Array<string>
    ):
      | { status: 'resolved'; collectionState: CollectionState }
      | { status: 'unresolvable'; reason: 'DOES_NOT_EXIST' };
    function getNearestCollectionState(
      path: Array<string>
    ):     | { status: 'resolved'; collectionState: CollectionState }
      | { status: 'unresolvable'; reason: 'DOES_NOT_EXIST' };
    function getAllTokenStates(): Array<TokenState>;
    function getAllGroupStates(): Array<GroupState>;
    function getAllCollectionStates(): Array<CollectionState>;
    function getAllNodeStates(): Array<TokenState | GroupState | CollectionState>;
    function getTokenChildrenOf(path: Array<string>): Array<TokenState>;
    function getGroupChildrenOf(path: Array<string>): Array<GroupState>;
    function getCollectionChildrenOf(path: Array<string>): Array<CollectionState>;
    function getChildrenOf(path: Array<string>, depth: number): Array<TokenState | GroupState | CollectionState>;
    function getParentsOf(path: Array<string>, depth: number): Array<TokenState | GroupState | CollectionState>;
    function getGroupChildren(path: Array<string>): Array<GroupState>;
    function getTokenChildren(path: Array<string>): Array<TokenState>;
    function getCollectionChildren(path: Array<string>): Array<CollectionState>;
    function renderJSONTree(renderOptions: RenderOptions): JSON;
    function getAliasReference(
      from: AliasReferenceCoordinates
    ): Result<AliasReference, SDTFError>;
    function getAllAliasReferences(): Array<AliasReference>;
    function getAliasReferencesTo(
      to: Partial<AliasReferenceCoordinates>,
      options: AliasReferenceResolvabilityOptions
    ): Array<AliasReference>;
    function getAliasReferencesFrom(
      from: Partial<AliasReferenceCoordinates>,
      options: AliasReferenceResolvabilityOptions
    ): Array<AliasReference>;
    function getStatefulAliasReference(
      from: AliasReferenceCoordinates
    ): Result<StatefulAliasReference, SDTFError>;
    function getStatefulAliasReferencesTo(
      to: Partial<AliasReferenceCoordinates>,
      options: AliasReferenceResolvabilityOptions
    ): Array<StatefulAliasReference>;
    function getStatefulAliasReferencesFrom(
      from: Partial<AliasReferenceCoordinates>,
      options: AliasReferenceResolvabilityOptions
    ): Array<StatefulAliasReference>;
    Parameter
    Required
    Type
    Default
    Description

    modes

    true

    Array

    The query that select items in the graph.

    hashtag
    Basic usage: select all tokens from a mode named "light"

    {
      "colors": {
    
    1. We want to get all design tokens from the mode named "light"

    2. We eventually generate our design tokens ass CSS variables in a CSS file thanks to the to-css-custom-properties parser.

    Parameter
    Required
    Type
    Default
    Description

    svgo

    false

    null

    The configuration.

    hashtag
    Basic usage

    {
      "icons": {
    
    menu.svg
    <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
        <path d="M9.25 9.25V3.5H4C3.72386 3.5 3.5 3.72386 3.5 4V9.25H9.25ZM9.25 10.75H3.5V16C3.5 16.2761 3.72386 16.5 4 16.5H9.25V10.75ZM10.75 16.5H16C16.2761 16.5 16.5 16.2761 16.5 16V10.75H10.75V16.5ZM4 18C2.89543 18 2 17.1046 2 16V4C2 2.89543 2.89543 2 4 2H10H16C17.1046 2 18 2.89543 18 4V10V16C18 17.1046 17.1046 18 16 18H4ZM16.5 9.25V4C16.5 3.72386 16.2761 3.5 16 3.5H10.75V9.25H16.5Z" fill="#788BA5" />
    </svg>
    {
      "version": "2",
    
    If you use the CLI, you need to fill three properties:
    • repository is @organization/repository

    • personalAccessToken which you can generate in your account settingsarrow-up-right

    • rules are where you provide parsers and compatible options

    If you use the GitHub, you need to fill 4 properties:

    • repository is @organization/repository

    • head lets you set the branch your PR will be created on

    • base lets you set the branch your PR will be merged on

    • rules lets you transform tokens by chaining parsers

    circle-info

    Make sure you have connected your GitHub account with your Specify account. Head toward to learn more.

    to-flutter
    svgo
    {
      "version": "2",
    

    prefix-by

    This parser helps you prefix the name of your collections, groups and tokens from your SDTF token tree.

    hashtag
    Interface

    hashtag
    Options

    Parameter
    Required
    Type
    Default
    Description

    hashtag
    Basic usage: Prefix color tokens

    to-json-list

    This parser helps you pull design tokens in JSON within an iterable array structure.

    hashtag
    Interface

    hashtag
    Options

    Parameter
    Required
    Type
    Default
    Description

    hashtag
    Return type

    The parser produces a custom data structure representing the flatten token tree.

    hashtag
    Basic usage

    Getting started

    Getting started with the Specify SDK.

    hashtag
    Prerequisites

    • NodeJS >= 18.x

    • [Optional] Typescript >= 4.9

    hashtag
    TypeScript vs JavaScript

    We recommend using TypeScript for a better developer experience. TypeScript's autocompletion in your editor can save you numerous trips to the documentation by suggesting methods and options from the Specify APIs.

    hashtag
    Installation

    Let's pretend you have a front-end application structured as follows:

    hashtag
    Create a specify directory

    You'll start by creating a specify directory at the application level.

    hashtag
    Initialize a JavaScript module

    Using your terminal, navigate to the directory.

    Within this directory, you initialize a JavaScript module by using the package manager of your choice - preferably the one you use to manage your application.

    circle-info

    If you use vanilla JavaScript, you can skip the installation of the typescript package.

    The init command creates a minimal package.json file where the dev dependencies will be added too. Then, you complete/override the content to match the following:

    circle-exclamation

    Note that "type": "module" is mandatory for the SDK to properly work.

    hashtag
    Setup TypeScript

    circle-info

    If you use vanilla JavaScript, you can skip this step.

    Within the specify directory, you create a tsconfig.json file which matches the following configuration:

    hashtag
    Prepare the environment secrets

    To interact with your Specify repositories you'll need to provide a Personal Access Token that you prefer to keep secret. For that reason, we recommend the use of the package.

    Thus, create a .env file within the specify directory:

    circle-exclamation

    If you use a version control utility like git, you most likely want to add the specify/.env file to your ignore configuration.

    circle-info

    Get a Personal Access Token from

    hashtag
    Create a Specify Client

    Within the specify directory, you create an extract.ts file. This TypeScript script will handle the extraction process by utilizing the personal access token specified in your .env file.

    circle-info

    If you use vanilla JavaScript, you can name this file extract.js.

    hashtag
    Authenticate

    In order to consume the private data from a Specify repository, you must authenticate using your personal access token stored in .env.

    hashtag
    Test your script

    From your terminal, launch the script.

    You should get a log such as:

    circle-info

    Your configuration does not run? Get in touch with us on .

    hashtag
    List your organization repositories

    Get a list of repositories belonging to your organization.

    suffix-by

    This parser helps you suffix the name of your collections, groups and tokens from your SDTF token tree.

    hashtag
    Interface

    hashtag
    Options

    Parameter
    Required
    Type
    Default
    Description

    hashtag
    Basic usage: Suffix color tokens

    SDTF Query Language

    SDTF Query Langage format API reference

    The SDTF Query Language is a JSON structure describing several match criteria used to select nodes - tokens, groups and collections - within the token tree.

    hashtag
    Examples

    Get a collection exactly named "Colors" and all its children of kind: "token"

    {
      "where": {
        "
    

    Select all tokens of type: "color". See all available types in .

    Select the tokens of type: "color", only within a group named "components" nested in a collection named "Colors".

    Notice it also selects the containing "components" group and "Colors" collection thanks to select.parents: true.

    hashtag
    Query Language Structure

    Every SDTFQuery holds a single a where property being:

    • an object: to select one branch of the graph

    • an array of objects: to select many branches of the graph - equivalent to an OR statement.

    The where property splits in 3 kinds: token, group, collection - offering a dedicated set of options to match against the given kind.

    hashtag
    Where Token

    hashtag
    Where Group

    hashtag
    Where Collection

    hashtag
    Recursion

    The where property can alternatively holds a andWhere property instead of the select property.

    Doing so, the andWhere property can receive any where operator described earlier.

    Note: since Collections cannot be nested, we cannot nest a andWhere Collection operator into another where or andWhere Collection

    hashtag
    Query Result

    Once executed by the engine, the query returns a that helps to work with the matched tree nodes.

    to-scss-map

    This parser helps you generate .scss files for each token type containing SCSS map and function / mixin to access the values of the tokens.

    hashtag
    Interface

    hashtag
    Options

    Parameter
    Required
    Type
    Default
    Description

    hashtag
    Basic usage

    Mutation API

    Methods for locally mutating the token tree.

    You access each of the mutation methods over:

    hashtag
    addToken

    Add a new token to the token tree.

    hashtag
    renameToken

    Rename a token.

    hashtag
    updateTokenDescription

    Update the description of a token.

    hashtag
    updateTokenExtensions

    Update the extensions of a token.

    hashtag
    updateTokenValue

    Update the value of a token.

    hashtag
    updateTokenModeValue

    Update the value of a token for a given mode.

    hashtag
    renameTokenMode

    Rename a token mode.

    hashtag
    createTokenModeValue

    Create a new token mode value.

    hashtag
    deleteTokenModeValue

    Delete a token mode value.

    hashtag
    deleteToken

    Delete a token.

    hashtag
    moveToken

    Move a token.

    hashtag
    addCollection

    Add a new collection to the token tree.

    hashtag
    renameCollection

    Rename a collection.

    hashtag
    updateCollectionDescription

    Update the description of a collection.

    hashtag
    updateCollectionExtensions

    Update the extensions of a collection.

    hashtag
    renameCollectionMode

    Rename a collection mode.

    hashtag
    truncateCollection

    Truncate a collection.

    hashtag
    deleteCollection

    Delete a collection.

    hashtag
    deleteCollectionMode

    Delete a collection mode.

    hashtag
    moveCollection

    Move a collection.

    hashtag
    addGroup

    Add a new group to the token tree.

    hashtag
    renameGroup

    Rename a group.

    hashtag
    updateGroupDescription

    Update the description of a group.

    hashtag
    updateGroupExtensions

    Update the extensions of a group.

    hashtag
    truncateGroup

    Truncate a group.

    hashtag
    deleteGroup

    Delete a group.

    hashtag
    moveGroup

    Move a group.

    svg-to-jsx

    This parser helps you generate JSX or TSX components from your vector assets.

    hashtag
    Interface

    hashtag
    Options

    Parameter
    Required
    Type
    Default
    Description

    hashtag
    Basic usage

    Tailwind

    This template helps you generate your design tokens as a Tailwind theme.

    This example uses the following parsers:

    • to-tailwind to generate your design tokens as a Tailwind theme

    • svg-to-jsx to generate icons as JSX components

    If you use the CLI, you need to fill three properties:

    • repository is @organization/repository

    • personalAccessToken which you can generate

    If you use the GitHub, you need to fill 4 properties:

    • repository is @organization/repository

    • head lets you set the branch your PR will be created on

    Tokens Studio

    In this guide you’ll learn how to sync your design tokens from Tokens Studio to your Specify repository and how to keep them updated.

    hashtag
    Before getting started

    To get the most out of this guide, you will need:

    • A account

    Parsers Engine

    Parsers Engine API reference

    hashtag
    Configuration file

    The Configuration file has common properties, and some destination specific ones

    hashtag

    register-view

    This parser helps you register a SDTF view that can be later used by generation parser to allow for partial outputs of the token tree with no destructive mutations.

    hashtag
    Interface

    hashtag
    Options

    to-json

    This parser helps you pull design tokens in JSON with token values in JSON or CSS.

    hashtag
    Interface

    hashtag
    Basic usage - JSON token values

    to-svg-file

    This parser helps you generate SVG files from your vector assets.

    hashtag
    Interface

    hashtag
    Options

    React Native

    This template helps you generate your design tokens as a React Native theme and icons as JSX components

    This example uses the following parsers:

    • to generate your design tokens as a React Native theme

    • to convert dimensions token from px to unitless values

    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Only get tokens from the mode named 'light' and gererate tokens in CSS",
          "parsers": [
            {
              "name": "select-modes",
              "options": {
                "modes": ["light"]
              }
            },
            {
              "name": "to-css-custom-properties",
              "options": {
                "selectorTemplate": "[data-theme=\"{{mode}}\"]"
              },
              "output": {
                "type": "file",
                "filePath": "public/css-custom-properties.css"
              }
            }
          ]
        }
      ]
    }
    tokens.json
    [data-theme="light"] {
      --danger-dangerToken: rgb(209, 204, 204);
      --info-infoToken: rgb(219, 234, 254);
    }
    interface parser {
      name: 'select-modes';
      options: {
        modes: string[];
      };
    }
    menu.svg
    <svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path d="M9.25 9.25V3.5H4C3.72386 3.5 3.5 3.72386 3.5 4V9.25H9.25ZM9.25 10.75H3.5V16C3.5 16.2761 3.72386 16.5 4 16.5H9.25V10.75ZM10.75 16.5H16C16.2761 16.5 16.5 16.2761 16.5 16V10.75H10.75V16.5ZM4 18C2.89543 18 2 17.1046 2 16V4C2 2.89543 2.89543 2 4 2H10H16C17.1046 2 18 2.89543 18 4V10V16C18 17.1046 17.1046 18 16 18H4ZM16.5 9.25V4C16.5 3.72386 16.2761 3.5 16 3.5H10.75V9.25H16.5Z" fill="currentColor" />
    </svg>
    interface Parser {
      name: "svgo";
      output: {
        type: "directory";
        directoryPath: string;
      };
      options?: {
        // https://github.com/svg/svgo/blob/main/lib/svgo.d.ts#L29-L49
        svgo?: Config;
      };
    }
    interface parser {
      name: 'prefix-by';
      options: ({
        all: string
      } | 
      {
        group?: string;
        collection?: string;
        token?: string;
      }) & { applyTo?: SDTFQuery }
    }
    interface parser {
      name: "to-json-list";
      output:
        | {
            type: "file";
            filePath: string;
          }
        | { type: "JSON" };
      options?: {
        stringPathSeparator?: string;
      };
    }
    
    interface parser {
      name: 'suffix-by';
      options: ({
        all: string
      } | 
      {
        group?: string;
        collection?: string;
        token?: string;
      }) & { applyTo?: SDTFQuery }
    }
    interface parser {
      name: 'to-scss-map';
      output: {
        type: 'directory';
        directoryPath: string;
      };
      options?: {
        tokenNameTemplate?: string;
      };
    }
    sdtfEngine.mutation.[method]
    function addToken(param:{
      parentPath: Array<string>,
      name: string,
      tokenProperties: {
        $type: string;
        $value: unknown;
        $description?: string;
        $extensions?: Record<string, unknown>;
      },
    }): void;
    interface parser {
      name: 'svg-to-jsx';
      output: {
        type: 'directory';
        directoryPath: string;
      };
      options?:{
        reactVersion?: string;
        tsx?: boolean;
        filePrefix?: string;
        fileSuffix?: string;
        exportDefault?: boolean;
        tokenNameTemplate?: string;
      }
    }
    "$collection": {
    "$modes": ["light", "dark"]
    },
    "info": {
    "infoToken": {
    "$type": "color",
    "$value": {
    "light": {
    "model": "rgb",
    "red": 219,
    "green": 234,
    "blue": 254,
    "alpha": 1
    },
    "dark": {
    "model": "rgb",
    "red": 219,
    "green": 234,
    "blue": 254,
    "alpha": 1
    }
    }
    }
    },
    "danger": {
    "dangerToken": {
    "$type": "color",
    "$value": {
    "light": {
    "model": "rgb",
    "red": 209,
    "green": 204,
    "blue": 204,
    "alpha": 1
    },
    "dark": {
    "model": "rgb",
    "red": 19,
    "green": 34,
    "blue": 54,
    "alpha": 1
    }
    }
    }
    }
    }
    }
    "array": {
    "$type": "vector",
    "$value": {
    "default": {
    "variationLabel": null,
    "format": "svg",
    "url": "https://static.specifyapp.com/sdtf-seeds/array.svg"
    }
    }
    }
    }
    }
    "repository": "@organization/repository",
    // Only use the personalAccessToken when working with the CLI
    "personalAccessToken": "<your-personal-access-token>",
    "rules": [
    {
    "name": "Optimize and transform SVG with svgo + Generate SVG files",
    "parsers": [
    {
    "name": "svgo",
    "output": {
    "type": "directory",
    "directoryPath": "output/assets"
    },
    "options": {
    "svgo": {
    "plugins": [
    {
    "name": "removeDimensions"
    },
    {
    "name": "convertColors",
    "params": {
    "currentColor": true
    }
    }
    ]
    }
    }
    }
    ]
    }
    ]
    }
    svgoarrow-up-right
    "repository": "@organization/repository",
    // Only use the personalAccessToken when working with the CLI
    "personalAccessToken": "<your-personal-access-token>",
    "rules": [
    {
    "name": "Generate tokens for Flutter",
    "parsers": [
    {
    "name": "to-flutter",
    "output": {
    "type": "file",
    "filePath": "public/tokens.dart"
    }
    }
    ]
    },
    {
    "name": "Optimize and generate icons as SVG files",
    "parsers": [
    {
    "name": "svgo",
    "output": {
    "type": "directory",
    "directoryPath": "public/vectors"
    }
    }
    ]
    }
    ]
    }
    this articlearrow-up-right

    stringPathSeparator

    false

    string

    "."

    Override the separator character used to build the string path property.

    dotenvarrow-up-right
    your user settings ↗︎arrow-up-right
    Discordarrow-up-right
    collection
    "
    :
    "
    ^Colors$
    "
    ,
    "select": {
    "collection": true,
    "children": {
    "tokens": true
    }
    }
    }
    }
    Supported types
    QueryResult
    {
      "where": {
        "token": ".*",
        "withTypes": { "include": ["color"] },
        "select": true
      }
    }

    false

    boolean

    false

    Set this to true if you want to generate a TSX component instead of JSX.

    filePrefix

    false

    string

    ''

    Add an arbitrary content at the beginning of the generated file.

    fileSuffix

    false

    string

    ''

    Add an arbitrary content at the end of the generated file.

    exportDefault

    false

    boolean

    true

    Whether use named or default export.

    tokenNameTemplate

    false

    string

    '{{token}}'

    Change the template of the generated named export.

    reactVersion

    false

    string

    The React version you're using. Depending on your version, this parser will automatically import React in your JSX component.

    tsx

    {
      "colors": {
        "$collection": {
          "$modes": [
            "light",
            "dark"
          ]
        },
        "Core": {
          "blue-100": {
            "$type": "color",
            "$value": {
              "dark": {
                "red": 229,
                "blue": 29,
                "alpha": 1,
                "green": 29,
                "model": "rgb"
              },
              "light": {
                "red": 255,
                "blue": 255,
                "alpha": 1,
                "green": 255,
                "model": "rgb"
              }
            },
            "$description": "token 1 aliased with n modes within collection within n groups"
          },
          "blue-700": {
            "$type": "color",
            "$value": {
              "dark": {
                "red": 229,
                "blue": 0,
                "alpha": 1,
                "green": 0,
                "model": "rgb"
              },
              "light": {
                "red": 255,
                "blue": 255,
                "alpha": 1,
                "green": 200,
                "model": "rgb"
              }
            },
            "$description": "token 2 aliased with n modes within collection within n groups"
          }
        },
        "semantic": {
          "background": {
            "button": {
              "primary": {
                "hover": {
                  "$type": "color",
                  "$value": {
                    "dark": {
                      "$mode": "dark",
                      "$alias": "colors.Core.blue-100"
                    },
                    "light": {
                      "$mode": "light",
                      "$alias": "colors.Core.blue-700"
                    }
                  },
                  "$description": "alias token with n modes within collection within n groups"
                }
              }
            }
          }
        }
      }
    }
    Parameter
    Required
    Type
    Default
    Description

    filenameTemplate

    false

    string

    "{{parents}}/{{name}}[-{{mode}}]{{extension}}"

    The mustache template used to generate the file path to write to the file system. Available variables: parents: group and collection names of the token's parents name: the name of the asset token mode: the name of the current mode extension: the file extension extracted from the token

    hashtag
    Basic usage - Single mode

    Our vector has a default mode and is in a group named "icons".

    The parser will generate the following output: {directoryPath}/{groups}/{vectorName}.svg

    hashtag
    Advanced usage - Multiple modes

    Our vector has a 2 modes: light and dark and is in a group named "icons".

    The parser will generate one file per mode:

    • {directoryPath}/{groups}/{vectorName}-{mode1}.svg

    • {directoryPath}/{groups}/{vectorName}-{mode2}.svg

    rules are where you provide parsers and compatible options

    base lets you set the branch your PR will be merged on

  • rules lets you transform tokens by chaining parsers

  • circle-info

    Make sure you have connected your GitHub account with your Specify account. Head toward this articlearrow-up-right to learn more.

    in your account settingsarrow-up-right
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Generate tokens as a Tailwind theme",
          "parsers": [
            {
              "name": "to-tailwind",
              "output": {
                "type": "file",
                "filePath": "theme.js"
              }
            }
          ]
        },
        {
          "name": "Optimize and transform vectors with svgo + Generate JSX components",
          "parsers": [
            {
              "name": "svgo",
              "options": {
                "svgo": {
                  "plugins": [
                    {
                      "name": "removeDimensions"
                    },
                    {
                      "name": "convertColors",
                      "params": {
                        "currentColor": true
                      }
                    }
                  ]
                }
              }
            },
            {
              "name": "svg-to-jsx",
              "output": {
                "type": "directory",
                "directoryPath": "output/assets"
              }
            }
          ]
        }
      ]
    }
    {
      "version": "2",
      "repository": "@organization/repository",
      "head": "specifyrc-json",
      "base": "main",
      "rules": [
        {
          "name": "Generate tokens for Flutter",
          "parsers": [
            {
              "name": "to-flutter",
              "output": {
                "type": "file",
                "filePath": "public/tokens.dart"
              }
            }
          ]
        },
        {
          "name": "Optimize and generate icons as SVG files",
          "parsers": [
            {
              "name": "svgo",
              "output": {
                "type": "directory",
                "directoryPath": "public/vectors"
              }
            }
          ]
        }
      ]
    }
    type FlattenNode = (
      | {
          kind: 'group';
        }
      | {
          kind: 'collection';
          modes: Array<string>;
        }
      | {
          kind: 'token';
          type: string;
          modeValues: Array<{
            mode: string;
            value: unknown;
          }>;
        }
    ) & {
      path: Array<string>;
      stringPath: string;
      name: string;
      description?: string;
      extensions?: unknown;
    };
    
    type ParserOutput = Array<FlattenNode>
    {
      "aCollection": {
        "$collection": {
          "$modes": ["light", "dark"]
        },
        "blue": {
          "$type": "color",
          "$value": {
            "light": {
              "model": "hex",
              "hex": "#0000CC",
              "alpha": 1
            },
            "dark": {
              "model": "hex",
              "hex": "#0000FF",
              "alpha": 1
            }
          }
        }
      },
      "aGroup": {
        "aDimension": {
          "$type": "dimension",
          "$value": {
            "default": {
              "unit": "px",
              "value": 16
            }
          }
        }
      }
    }
    
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "To JSON",
          "parsers": [
            {
              "name": "to-json-list",
              "output": {
                "type": "file",
                "filePath": "tokens.json"
              }
            }
          ]
        }
      ]
    }
    tokens.json
    [
      {
        "path": [
          "aCollection"
        ],
        "stringPath": "aCollection",
        "name": "aCollection",
        "kind": "collection",
        "modes": [
          "light",
          "dark"
        ]
      },
      {
        "path": [
          "aCollection",
          "blue"
        ],
        "stringPath": "aCollection.blue",
        "name": "blue",
        "kind": "token",
        "type": "color",
        "modeValues": [
          {
            "mode": "dark",
            "value": {
              "model": "hex",
              "hex": "#0000FF",
              "alpha": 1
            }
          },
          {
            "mode": "light",
            "value": {
              "model": "hex",
              "hex": "#0000CC",
              "alpha": 1
            }
          }
        ]
      },
      {
        "path": [
          "aGroup"
        ],
        "stringPath": "aGroup",
        "name": "aGroup",
        "kind": "group"
      },
      {
        "path": [
          "aGroup",
          "aDimension"
        ],
        "stringPath": "aGroup.aDimension",
        "name": "aDimension",
        "kind": "token",
        "type": "dimension",
        "modeValues": [
          {
            "mode": "default",
            "value": {
              "value": 16,
              "unit": "px"
            }
          }
        ]
      }
    ]
    my-app/
        src/
            components/
                ...
            index.ts
        public/
        package.json
        ...
    my-app/
        specify/
            ...
        src/
            components/
                ...
            index.ts
        public/
        package.json
        ...
    cd ./specify
    npm init
    npm install -D @specifyapp/sdk dotenv typescript
    yarn init
    npm add -D @specifyapp/sdk dotenv typescript
    pnpm init
    pnpm add -D @specifyapp/sdk dotenv typescript
    specify/package.json
    {
      "name": "specify-design-tokens-extractor",
      "private": true,
      "version": "1.0.0",
      "description": "Extract design tokens from Specify repositories",
      "type": "module",
      "scripts": {
        "build": "tsc",
        "extract": "tsc && node ./extract.js"
      }
    }
    specify/package.json
    {
      "name": "specify-design-tokens-extractor",
      "private": true,
      "version": "1.0.0",
      "description": "Extract design tokens from Specify repositories",
      "type": "module",
      "scripts": {
        "extract": "node ./extract.js"
      }
    }
    {
      "compilerOptions": {
        "strict": true,
        "target": "es2022",
        "lib": ["es2022"],
        "moduleDetection": "force",
        "module": "NodeNext",
        "sourceMap": true,
        "skipLibCheck": true,
        "outDir": ".",
        "rootDir": "./"
      },
      "include": ["./extract.ts"],
      "exclude": ["node_modules"]
    }
    specify/.env
    SPECIFY_PERSONAL_ACCESS_TOKEN=YOUR-PERSONAL-ACCESS-TOKEN
    specify/extract.ts
    import { config } from "dotenv";
    import { createSpecifyClient, updaters, parsers } from "@specifyapp/sdk";
    
    // Load Personal Access Token from .env file
    const { parsed } = config({ path: ".env" });
    specify/extract.ts
    // Initialize Specify Client
    const specifyClient = createSpecifyClient();
    await specifyClient.authenticate(parsed?.SPECIFY_PERSONAL_ACCESS_TOKEN || "");
    
    console.log(`User authenticated: ${specifyClient.whoAmI()?.email}`);
    npm run extract
    yarn extract
    pnpm run extract
    User authenticated: [email protected]
    const repositories = await specifyClient.getRepositories();
    
    console.log(repositories); // [{ id: '...', name: '...' }, ...]
    {
      "where": {
        "collection": "^Colors$",
        "andWhere": {
          "group": "^components$",
          "andWhere": {
            "token": ".*",
            "withTypes": { "include": ["color"] },
            "select": {
              "token": true,
              "parents": true
            }
          }
        }
      }
    }
    type SDTFQuery = { where: Where | Array<Where> }
    type WhereToken = {
      token:
        | string // regex compatible
        | {
          name?: string; // regex compatible
          description?: string; // regex compatible
        };
      select:
        | true
        | {
          token?: boolean;
          parents?:
            | true
            | {
              groups?: true;
              collections?: true;
            };
        };
      withTypes?: {
        include?: Array<TokenTypeName>;
        exclude?: Array<TokenTypeName>;
      };
      withModes?: {
        include?: Array<string>;
        exclude?: Array<string>;
      }
    };
    type WhereGroup = {
      group:
        | string // regex compatible
        | {
          name?: string; // regex compatible
          description?: string; // regex compatible
        };
      select:
        | true
        | {
        group?: boolean;
        parents?:
          | true
          | {
            groups?: true;
            collections?: true;
          };
        children:
          | true
          | {
            groups?: true;
            collections?: true;
            tokens?: true;
          };
      };
    };
    type WhereCollection = {
      collection:
        | string // regex compatible
        | {
          name?: string; // regex compatible
          description?: string; // regex compatible
        };
      select:
        | true
        | {
          collection?: boolean;
          parents?:
            | true
            | {
              groups?: true;
            };
          children:
            | true
            | {
              groups?: true;
              tokens?: true;
            };
        };
      withModes?: {
        include?: Array<string>;
        exclude?: Array<string>;
      }
    };
    {
      "where": {
        "collection": "^colors$",
        "andWhere": {
          "group": "^base",
          "andWhere": {
            "token": ".*",
            "withTypes": { "include": ["color"] },
            "select": {
              "token": true,
              "parents": true
            }
          }
        }
      }
    }
    function renameToken(param: { atPath: Array<string>; name: string }): void;
    function updateTokenDescription(param: {
      atPath: Array<string>;
      description: string;
    }): void;
    function updateTokenExtensions(param: {
      atPath: Array<string>;
      extensions: treeNodeExtensionsSchema;
    }): void;
    function updateTokenValue(param:{
      atPath: Array<string>,
      // We cannot know with type of the value since no type is enforced
      value: unknown,
    }): void;
    function updateTokenModeValue(param:{
        mode: string,
        // We cannot know with type of the value since no type is enforced
        value: unknown,
    }): void;
    function renameTokenMode(param: {
      atPath: Array<string>;
      fromMode: string;
      toMode: string;
    }): void;
    function createTokenModeValue(param:{
      atPath: Array<string>,
      mode: string,
      // We cannot know with type of the value since no type is enforced
      value: unknown,
    }): void;
    function deleteTokenModeValue(param: {
      atPath: Array<string>;
      mode: string;
    }): void;
    function deleteToken(param: { atPath: Array<string> }): void;
    function moveToken(param: {
      atPath: Array<string>;
      toPath: Array<string>;
    }): void;
    function addCollection(param: {
      parentPath: Array<string>;
      name: string;
      collectionProperties: { 
        $collection: {$modes: Array<string>};
        $description?: string;
        $extensions?: Record<string, unknown>;
      };
    }): void;
    function renameCollection(param: {
      atPath: Array<string>;
      name: string;
    }): void;
    function updateCollectionDescription(param: {
      atPath: Array<string>;
      description: string;
    }): void;
    function updateCollectionExtensions(param: {
      atPath: Array<string>;
      extensions: treeNodeExtensionsSchema;
    }): void;
    function renameCollectionMode(param: {
      atPath: Array<string>;
      fromMode: string;
      toMode: string;
    }): void;
    function truncateCollection(param: { atPath: Array<string> }): void;
    function deleteCollection(param: { atPath: Array<string> }): void;
    function deleteCollectionMode(param: {
      atPath: Array<string>;
      mode: string;
    }): void;
    function moveCollection(param: {
      atPath: Array<string>;
      toPath: Array<string>;
    }): void;
    function addGroup(param: {
      parentPath: Array<string>;
      name: string;
      groupProperties: specifyGroupPropertiesSchema;
    }): void;
    function renameGroup(param: { atPath: Array<string>; name: string }): void;
    function updateGroupDescription(param: {
      atPath: Array<string>;
      description: string;
    }): void;
    function updateGroupExtensions(param: {
      atPath: Array<string>;
      extensions: treeNodeExtensionsSchema;
    }): void;
    function truncateGroup(param: { atPath: Array<string> }): void;
    function deleteGroup(param: { atPath: Array<string> }): void;
    function moveGroup(param: {
      atPath: Array<string>;
      toPath: Array<string>;
    }): void;
    17.0.0
    {
      "icons": {
        "menu": {
          "$type": "vector",
          "$value": {
            "default": {
              "variationLabel": null,
              "format": "svg",
              "url": "<url-of-your-svg-file>"
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Generate JSX components",
          "parsers": [
            {
              "name": "svg-to-jsx",
              "output": {
                "type": "directory",
                "directoryPath": "output/assets"
              }
            }
          ]
        }
      ]
    }
    output/assets/icons/menu.jsx
    export default () => (
        <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
            <path d="M9.25 9.25V3.5H4C3.72386 3.5 3.5 3.72386 3.5 4V9.25H9.25ZM9.25 10.75H3.5V16C3.5 16.2761 3.72386 16.5 4 16.5H9.25V10.75ZM10.75 16.5H16C16.2761 16.5 16.5 16.2761 16.5 16V10.75H10.75V16.5ZM4 18C2.89543 18 2 17.1046 2 16V4C2 2.89543 2.89543 2 4 2H10H16C17.1046 2 18 2.89543 18 4V10V16C18 17.1046 17.1046 18 16 18H4ZM16.5 9.25V4C16.5 3.72386 16.2761 3.5 16 3.5H10.75V9.25H16.5Z" fill="#788BA5" />
        </svg>
    );
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "To JSON",
          "parsers": [
            {
              "name": "to-json",
              "output": {
                "type": "file",
                "filePath": "tokens.json"
              }
            }
          ]
        }
      ]
    }
    tokens.json
    {
      "colors": {
        "Core": {
          "blue-100": {
            "dark": {
              "model": "rgb",
              "red": 229,
              "green": 29,
              "blue": 29,
              "alpha": 1
            },
            "light": {
              "model": "rgb",
              "red": 255,
              "green": 255,
              "blue": 255,
              "alpha": 1
            }
          },
          "blue-700": {
            "dark": {
              "model": "rgb",
              "red": 229,
              "green": 0,
              "blue": 0,
              "alpha": 1
            },
            "light": {
              "model": "rgb",
              "red": 255,
              "green": 200,
              "blue": 255,
              "alpha": 1
            }
          }
        },
        "semantic": {
          "background": {
            "button": {
              "primary": {
                "hover": {
                  "dark": {
                    "model": "rgb",
                    "red": 229,
                    "green": 29,
                    "blue": 29,
                    "alpha": 1
                  },
                  "light": {
                    "model": "rgb",
                    "red": 255,
                    "green": 200,
                    "blue": 255,
                    "alpha": 1
                  }
                }
              }
            }
          }
        }
      }
    }
    interface parser {
      name: 'to-json';
      output: {
        type: 'file';
        filePath: string;
      };
      options?: {
        output?: 'raw' | 'css';
      };
    }
    {
      "icons": {
        "menu": {
          "$type": "vector",
          "$value": {
            "default": {
              "variationLabel": null,
              "format": "svg",
              "url": "<url-of-your-svg-file>"
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Generate SVG files",
          "parsers": [
            {
              "name": "to-svg-file",
              "output": {
                "type": "directory",
                "directoryPath": "output/assets"
              }
            }
          ]
        }
      ]
    }
    output/assets/icons/menu.svg
    <svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
        <path d="M9.25 9.25V3.5H4C3.72386 3.5 3.5 3.72386 3.5 4V9.25H9.25ZM9.25 10.75H3.5V16C3.5 16.2761 3.72386 16.5 4 16.5H9.25V10.75ZM10.75 16.5H16C16.2761 16.5 16.5 16.2761 16.5 16V10.75H10.75V16.5ZM4 18C2.89543 18 2 17.1046 2 16V4C2 2.89543 2.89543 2 4 2H10H16C17.1046 2 18 2.89543 18 4V10V16C18 17.1046 17.1046 18 16 18H4ZM16.5 9.25V4C16.5 3.72386 16.2761 3.5 16 3.5H10.75V9.25H16.5Z" />
    </svg>
    {
      "asset": {
        "$collection": {
          "$modes": [
            "light",
            "dark"
          ]
        },
        "icons": {
          "menu": {
            "$type": "vector",
            "$value": {
              "light": {
                "variationLabel": null,
                "format": "svg",
                "url": "<url-of-your-svg-file>"
              },
              "dark": {
                "variationLabel": null,
                "format": "svg",
                "url": "<url-of-your-svg-file>"
              }
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Generate SVG files",
          "parsers": [
            {
              "name": "to-svg-file",
              "output": {
                "type": "directory",
                "directoryPath": "output/assets"
              }
            }
          ]
        }
      ]
    }
    output/assets/icons/menu-light.svg
    <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path d="M9.25 9.25V3.5H4C3.72386 3.5 3.5 3.72386 3.5 4V9.25H9.25ZM9.25 10.75H3.5V16C3.5 16.2761 3.72386 16.5 4 16.5H9.25V10.75ZM10.75 16.5H16C16.2761 16.5 16.5 16.2761 16.5 16V10.75H10.75V16.5ZM4 18C2.89543 18 2 17.1046 2 16V4C2 2.89543 2.89543 2 4 2H10H16C17.1046 2 18 2.89543 18 4V10V16C18 17.1046 17.1046 18 16 18H4ZM16.5 9.25V4C16.5 3.72386 16.2761 3.5 16 3.5H10.75V9.25H16.5Z" fill="#788BA5" />
    </svg>
    output/assets/icons/menu-dark.svg
    <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path d="M9.25 9.25V3.5H4C3.72386 3.5 3.5 3.72386 3.5 4V9.25H9.25ZM9.25 10.75H3.5V16C3.5 16.2761 3.72386 16.5 4 16.5H9.25V10.75ZM10.75 16.5H16C16.2761 16.5 16.5 16.2761 16.5 16V10.75H10.75V16.5ZM4 18C2.89543 18 2 17.1046 2 16V4C2 2.89543 2.89543 2 4 2H10H16C17.1046 2 18 2.89543 18 4V10V16C18 17.1046 17.1046 18 16 18H4ZM16.5 9.25V4C16.5 3.72386 16.2761 3.5 16 3.5H10.75V9.25H16.5Z" fill="#555AAA" />
    </svg>
    interface parser {
      name: 'to-svg-file';
      options: {
        filenameTemplate: string;
      };
      output: {
        type: 'directory';
        directoryPath: string;
      };
    }
    {
      "version": "2",
      "head": "specifyrc-json",
      "base": "main",
      "repository": "@organization/repository",
      "rules": [
        {
          "name": "Generate tokens as a Tailwind theme",
          "parsers": [
            {
              "name": "to-tailwind",
              "output": {
                "type": "file",
                "filePath": "theme.js"
              }
            }
          ]
        },
        {
          "name": "Optimize and transform vectors with svgo + Generate JSX components",
          "parsers": [
            {
              "name": "svgo",
              "options": {
                "svgo": {
                  "plugins": [
                    {
                      "name": "removeDimensions"
                    },
                    {
                      "name": "convertColors",
                      "params": {
                        "currentColor": true
                      }
                    }
                  ]
                }
              }
            },
            {
              "name": "svg-to-jsx",
              "output": {
                "type": "directory",
                "directoryPath": "output/assets"
              }
            }
          ]
        }
      ]
    }

    false

    string

    Select all groups in your SDTF token graph.

    collection

    false

    string

    Select all collections in your SDTF token graph.

    token

    false

    string

    Select all tokens in your SDTF token graph.

    applyTo

    false

    The selection where to apply the transformation. collection, group, token take a Regex string or true to select anything of the kind. An can be used for advance use cases.

    all

    false

    string

    Select all collections, groups and tokens in your SDTF token graph.

    group

    false

    string

    Select all groups in your SDTF token graph.

    collection

    false

    string

    Select all collections in your SDTF token graph.

    token

    false

    string

    Select all tokens in your SDTF token graph.

    applyTo

    false

    The selection where to apply the transformation. collection, group, token take a Regex string or true to select anything of the kind. An can be used for advance use cases.

    all

    false

    string

    Select all collections, groups and tokens in your SDTF token graph.

    group

  • The Tokens Studioarrow-up-right plugin installed in your Figma

  • A GitHubarrow-up-right, GitLabarrow-up-right, Azure DevOpsarrow-up-right, or JSONBinarrow-up-right account

  • Specify automatically fetches design tokens through the JSON file created by Tokens Studio. The best way to keep your design tokens in sync with both tools is to host your JSON file in a repository like GitHub.

    hashtag
    1. Sync your design tokens from Tokens Studio to a provider

    1. Head to your Tokens Studio plugin in Figma

    2. Within the settings tab, add a new sync provider

    3. Commit your Tokens Studio JSON file to your repository

    circle-info

    You can also manually export your file from Tokens Studio and upload it manually to your code repository. Click on Tools on the bottom left of the plugin and Export to file/folder. Be careful to tick all the boxes before exporting. We are not supporting multiple files at the moment.

    hashtag
    2. Add your JSON file from a provider to your Specify Repository

    1. Go to your Specify workspace

    2. Click on "Create repository"

    3. Choose "Advanced Repository"

    4. Name your repository

    5. Select "Sync from Figma Variables & Tokens Studio" ()

    6. Click on "Create repository"

    7. In the "Source" tab, click on "Create a source"

    8. Select "Remote URL"

    At this point, you have two ways to sync your JSON file. Either with a public hosting link or a private one. We will go through both options below.

    hashtag
    From a public URL

    1. In the "Source" tab, click on "Create a source"

    2. Select "Remote URL"

    3. Select "Public"

    4. Name your source

    5. Paste your raw public URL of your JSON file

    6. Select the format "Tokens Studio"

    7. Let Specify check the connection

    8. And voila!

    Your JSON file is now detected as a source and your design tokens appear within your repository.

    hashtag
    From a private URL

    On the opposite of the public URL, Specify will ask you for some additional information so its system is able to fetch your file. Let’s see how to proceed with the main versioning tools:

    • GitHub

    • Azure DevOps

    • GitLab

    hashtag
    GitHub

    Requirements:

    • Have a GitHub account

    • Have an Advanced Repository created

    • Have a JSON file containing design tokens from Tokens Studio

    To add a private URL source from GitHub to Specify:

    1. In the "Source" tab of your Specify repository, click on "Create a source"

    2. Select "Remote URL"

    3. Select "Private"

    4. Name your source

    5. Create and Paste this GitHub file URL such as: https://api.github.com/repos/{owner}/{repo}/contents/{file_path}

    6. Select "Bearer Token" as the auth system & paste your personal access token from GitHub ( and be sure to check the repo section)

    7. Select "Tokens Studio Format"

    8. Specify will test your JSON

    9. And voila!

    hashtag
    Azure DevOps

    Requirements:

    • Have an Azure DevOps account

    • Have a Project containing a repository

    • Have a JSON file containing design tokens from Tokens Studio

    To add a private URL source from Azure DevOps to Specify:

    1. In the "Source" tab of your Specify repository, click on "Create a source"

    2. Select "Remote URL"

    3. Select "Private"

    4. Name your source

    5. Paste your Azure DevOps file URL such as https://dev.azure.com/{OrgName}/{ProjectName}/_apis/git/repositories/{RepositoryName}/items?path={FilePath}&api-version=7.0&includeContent=true

    6. Select "Basic Auth" as auth system. Use your Azure DevOps email and a new Azure DevOps personal access token with Full Access or Code -> Read permissions.

    7. Select "Tokens Studio Format"

    8. Specify will test your JSON

    9. And voila!

    hashtag
    GitLab

    Requirements:

    • Have a GitLab account

    • Have an Advanced Repository created

    • Have a JSON file containing design tokens from Tokens Studio

    To add a private URL source from GitLab to Specify:

    1. In the "Source" tab of your Specify repository, click on "Create a source"

    2. Select "Remote URL"

    3. Select "Private"

    4. Give a name to your source

    5. Paste your GitLab file URL such as: https://gitlab.com/api/v4/projects/{OrgName}%2F{RepositoryName}/repository/files/{FilePath}?ref={branch}

    6. Create an access token in GitLab: "Settings > Access Tokens". Select a role as Developer or Owner and select the scopes of read_api and read_repository.

    7. In Specify, select Header as auth system

      1. Fill PRIVATE-TOKEN in the key field

    8. Select "Tokens Studio Format"

    9. Specify will test your JSON

    10. And voila!

    hashtag
    JSONBin

    Requirements:

    • Have a JSONBin account

    • Have an Advanced Repository created

    • Have a bin with a JSON file containing design tokens from Tokens Studio

    To add a private URL source from JSONBin to Specify:

    1. In the "Source" tab of your Specify repository, click on "Create a source"

    2. Select "Remote URL"

    3. Select "Private"

    4. Name your source

    5. Paste your BIN private URL such as https://api.jsonbin.io/v3/b/{bin_id}

    6. Select Header as auth system

    7. Depending on your choice, you can use your master key or an access key. on JSONBin.

      1. Following your choice, fill in the key field either with X-MASTER-KEY or X-ACCESS-KEY

    8. Select "Tokens Studio Format"

    9. Specify will test your JSON

    10. And voila!

    hashtag
    3. How to update your JSON File

    After adding your source. All you have to do is to:

    1. Go to the "Source" tab of your Specify repository

    2. Click on the context menu next to your source

    3. Click on "sync"

    Your source is now updated!

    hashtag
    4. Non-supported design token types

    Please keep in mind Specify is yet not compatible with the following data coming from Tokens Studio:

    • Composition

    • Assets (bitmap & vectors)

    • Color manipulation (gradients, alpha, darken, lighten & mix)

    They will be released in future updates. However, if you have urgent needs for Specify to be compatible with one of them, feel free to send us feedbackarrow-up-right.

    Specifyarrow-up-right
    For Specify CLI

    hashtag
    For GitHub

    hashtag
    ParserRule

    The Parsers Rules help you transform your design tokens and assets the way you want.

    A rule is composed of the following properties:

    Name
    Type
    Required
    Description

    name

    string

    false

    The name of your rule.

    parsers

    circle-info

    In many places you will use many parser rules in a configuration to parallelize the changes you want to apply.

    hashtag
    BuiltInParserRuleConfiguration

    The Parser Configuration object helps you configure the behaviour of a given parser.

    circle-info

    Looking for the parsers list and options? 👉 heads up to the Parsers reference

    The object is composed of the following properties:

    Name
    Type
    Required
    Description

    name

    string

    true

    The name of the parser. Choose from .

    options

    hashtag
    ParsersEngineDataBox

    The parsers engine can take several input types called parsers engine data boxes. On start, the Parsers engine requires one of the data box types to be passed in as initial input.

    hashtag
    SDTF

    hashtag
    SDTF Engine

    hashtag
    Specify Repository

    hashtag
    JSON

    hashtag
    Vector

    hashtag
    Bitmap

    hashtag
    Asset

    hashtag
    ParserOutput

    The parsers produce different types of outputs based on your configuration and their proper capabilities. If not all the parsers accept an output configuration property, the ones which do always take a standardized type as input.

    hashtag
    File

    Use case: the parser is expected to produce exactly one file.

    Example with the to-css-custom-properties parser:

    hashtag
    Directory

    Use case: the parser is expected to produce 0 to N files, all placed in the given base directory.

    Example with the to-svg-file parser:

    Parameter
    Required
    Type
    Default
    Description

    name

    true

    string

    The name of the SDTF view.

    hashtag
    Basic usage: register a SDTF view for use with CSS output

    {
      "colors": {
    
    1. We want to get all tokens in all groups named "info"

    2. We also want to get the parent collection...

    3. ... and all children tokens within the "info" group(s)

    4. We eventually generate our selected SDTF token tree in a CSS file thanks to the to-css-custom-properties parser.

    svg-to-jsx to generate icons as JSX components

    If you use the CLI, you need to fill three properties:

    • repository is @organization/repository

    • personalAccessToken which you can generate in your account settingsarrow-up-right

    • rules are where you provide parsers and compatible options

    If you use the GitHub, you need to fill 4 properties:

    • repository is @organization/repository

    • head lets you set the branch your PR will be created on

    to-react-native
    convert-dimension

    tokenNameTemplate

    false

    string

    {{path}}-{{token}}-{{mode}}

    The template the parser follows to name your tokens. You can use the path of your tokens, their token name, and their respective mode.

    {
      "colors": {
        "$collection": { "$modes": ["light", "dark
        "core": {
          "blue-100": {
            "$type": "color",
            "$description": "token 1 aliased with n modes within collection within n groups",
            "$value": {
              "light": {
                "red": 219,
                "blue": 254,
                "alpha": 1,
                "green": 236,
                "model": "rgb"
              },
              "dark": {
                "red": 41,
                "blue": 67,
                "alpha": 1,
                "green": 52,
                "model": "rgb"
              }
            }
          },
          "blue-700": {
            "$type": "color",
            "$description": "token 2 aliased with n modes within collection within n groups",
            "$value": {
              "light": {
                "red": 17,
                "blue": 249,
                "alpha": 1,
                "green": 125,
                "model": "rgb"
              },
              "dark": {
                "red": 96,
                "blue": 250,
                "alpha": 1,
                "green": 168,
                "model": "rgb"
              }
            }
          }
        },
        "semantic": {
          "background": {
            "button": {
              "primary": {
                "hover": {
                  "$type": "color",
                  "$description": "alias token with n modes within collection within n groups",
                  "$value": {
                    "dark": {
                      "$mode": "dark",
                      "$alias": "colors.core.blue-100"
                    },
                    "light": {
                      "$mode": "light",
                      "$alias": "colors.core.blue-700"
                    }
                  }
                }
              }
            }
          }
        }  
      }
    }

    Manage font files

    Learn how to add font files to your Specify repositories once you've synced text styles.

    hashtag
    Introduction

    Sources like Figma and Tokens Studio manages textStyle and font token types as parameters applied to the fonts available on the host machine.

    For both technical and legal reasons, Specify cannot extract font files from those sources, even though the SDTF is able to manage font files natively.

    To bridge that gap, we'll share with you some techniques you can leverage to keep your font files in sync with your font and text styles tokens.

    hashtag
    Prerequisites

    Before we start you need:

    • A Specify account with a repository

    • One or more font files in the following formats: ttf, woff, woff2, otf, eot.

    hashtag
    #1 The easy way: upload font files in Specify

    hashtag
    #2 Add font files by creating your own SDTF graph

    hashtag
    1. Host your font files

    You are free to host your font files anywhere you can grab a public link to give Specify the ability to fetch the binaries.

    We recommend using a company cloud storage like Amazon S3.

    hashtag
    2. Create a SDTF tokens file

    To register your font files to Specify, you need to create a SDTF JSON file containing your font tokens with the URLs from your cloud provider.

    circle-info

    Note that you want to make the family property to match the font family used by your TextStyle or Font tokens.

    hashtag
    3. Host your SDTF tokens file

    To let Specify synchronize the font tokens into your repository, the SDTF JSON file must be hosted either privately or publicly.

    hashtag
    Hosting on GitHub

    1. Create a new public or private repository on .

    2. Add your SDTF JSON file containing the font tokens

    1. Grab the raw file URL by accessing the raw file over your browser

    hashtag
    Hosting on JSONBin

    1. Create a new private or public bin on .

    2. Place the content of your SDTF JSON file within it

    1. Grab the URL

    hashtag
    4. Synchronize your tokens with Specify

    You can now navigate to your Specify repository and head to "Sources" -> "Create source"

    1. Select the "Remote URL - Tokens Studio"

    1. Provide the URL to the font tokens file you created at step 3.

    1. (Optional) Provide authentication credentials for your provider. Read more about managing .

    2. Select the "Specify Design Tokens Format" option

    1. Create the source. The sync is executed on the source creation and should take few seconds.

    hashtag
    5. Sync the font files in your codebase

    Once your new font tokens are synced into your Specify repository, you can start pulling the font-files to your codebase.

    Let's have a look to one of the that can be used into any configuration file:

    Once executed, the font files are available within the ./public/fonts directory.

    to-flutter

    This parser helps you generate a Flutter theme from all your design tokens coming from Specify.

    hashtag
    Interface

    hashtag
    Options

    Parameter
    Required
    Type
    Default
    Description

    hashtag
    Basic usage

    to-swift

    This parser helps you generate design tokens as Swift classes.

    hashtag
    Interface

    hashtag
    Options

    Parameter
    Required
    Type
    Default
    Description

    hashtag
    Basic usage

    to-javascript

    This parser helps you pull design tokens as JavaScript objects for all token types and their respective helper functions.

    hashtag
    Interface

    interface parser {
      name: 'to-javascript';
      output: {
        type: 'file';
        filePath: string;
      };
      options?: {
        typescript?: boolean;
        moduleExport?: 'es6' | 'commonjs';
      };
    }

    hashtag
    Basic usage

    to-sdtf

    This parser helps you get your design tokens as a SDTF graph in JSON.

    hashtag
    Interface

    interface parser {
      name: 'to-sdtf';
      output:
        | { type: 'directory'; directoryPath: string }
        | { type: 'file'; filePath: string }
        | { type: 'SDTF' }
        | { type: 'text' }
        | { type: 'JSON'; filePath: string };
    }

    hashtag
    Basic usage

    to-typescript

    This parser helps you pull design tokens as TypeScript objects for all token types and their respective helper functions.

    hashtag
    Interface

    interface parser {
      name: 'to-typescript';
      output: {
        type: 'file';
        filePath: string;
      };
      options?: {
        moduleExport?: 'es6' | 'commonjs';
      };
    }

    hashtag
    Basic usage

    to-style-dictionary

    This parser helps you generate Style Dictionary raw token files for all your design tokens coming from Specify.

    Unlike the existing to-style-dictionary parserarrow-up-right, this one doesn't have any options yet.

    hashtag
    Interface

    hashtag
    General output rules

    • A collection will generate a folder at the top level

      1. The default level refers to the SDTF Token Type associated SD Category → {collectionName?}/{SDCategory}

    hashtag
    Basic usage

    to-css-font-import

    This parser helps you create CSS @font-face rules to import your font files.

    hashtag
    Interface

    hashtag
    Options

    Parameter
    Required
    Type
    Default
    Description

    hashtag
    Basic usage

    filter

    This parser helps you filter a SDTF graph.

    hashtag
    Interface

    hashtag
    Options

    replace-string

    This parser helps you replace a part or the whole name of your collections, groups and tokens from your SDTF token tree.

    hashtag
    Interface

    hashtag
    Options

    Converting a token to XXX

    If your goal is to create your own output, you'll have to go through 2 steps:

    • Converting the tokens to the desired output, e.g: CSS, Js, Swift, etc...

    • Formatting the converted tokens into the desired template

    We can't know what's the perfect template for your needs, but we can definitely help you converting tokens to your desired output. That's why we created the converters.

    Executing generation parsers

    When it comes to generate code from your with the , you can leverage Specify's built-in and / or add your own custom implementation.

    The SDK provides two main methods to face either the need of the flexibility or increased performance.

    By default, the generation is run locally utilizing the host machine resources. To meet some environment limitation, any Specify's built-in parser can be executed remotely, on Specify servers.

    hashtag
    Create parsers pipelines

    to-css-text-style

    This parser helps you generate text styles as CSS classes.

    hashtag
    Interface

    hashtag
    Options

    to-scss-mixin-text-style

    This parser helps you generate text styles as SCSS mixins.

    hashtag
    Interface

    hashtag
    Options

    {
      "spacing": {
        "1": {
          "$type": "dimension",
          "$value": {
            "default": {
              "value": 4,
              "unit": "px"
            }
          }
        }
      },
      "colors": {
        "black": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#000000",
              "alpha": 1
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Prefix color tokens",
          "parsers": [
            {
              "name": "prefix-by",
              "options": {
                "token": "ds-",
                "applyTo": {
                  "where": {
                    "token": ".*",
                    "withTypes": {
                      "include": [
                        "color"
                      ]
                    },
                    "select": {
                      "parents": true
                    }
                  }
                }
              }
            },
            {
              "name": "to-sdtf",
              "output": {
                "type": "file",
                "filePath": "tokens.json"
              }
            }
          ]
        }
      ]
    }
    tokens.json
    {
      "colors": {
        "ds-black": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#000000",
              "alpha": 1
            }
          }
        }
      },
      "spacing": {
        "1": {
          "$type": "dimension",
          "$value": {
            "default": {
              "value": 4,
              "unit": "px"
            }
          }
        }
      }
    }
    {
      "spacing": {
        "1": {
          "$type": "dimension",
          "$value": {
            "default": {
              "value": 4,
              "unit": "px"
            }
          }
        }
      },
      "colors": {
        "black": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#000000",
              "alpha": 1
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Prefix color tokens",
          "parsers": [
            {
              "name": "suffix-by",
              "options": {
                "token": "-color",
                "applyTo": {
                  "where": {
                    "token": ".*",
                    "withTypes": {
                      "include": [
                        "color"
                      ]
                    },
                    "select": {
                      "parents": true
                    }
                  }
                }
              }
            },
            {
              "name": "to-sdtf",
              "output": {
                "type": "file",
                "filePath": "tokens.json"
              }
            }
          ]
        }
      ]
    }
    tokens.json
    {
      "colors": {
        "black-color": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#000000",
              "alpha": 1
            }
          }
        }
      },
      "spacing": {
        "1": {
          "$type": "dimension",
          "$value": {
            "default": {
              "value": 4,
              "unit": "px"
            }
          }
        }
      }
    }
    interface ConfigurationFileCommon {
      version: '2';
      repository: string; // @owner/repository
      rules: Array<ParserRule>;
    }
    interface CLIConfigurationFile extends ConfigurationFileCommon {
      personalAccessToken?: string; // Can be set using the -p flag
    }
    interface GitHubConfigurationFile extends ConfigurationFileCommon {
      personalAccessToken: string; // Can be set over secret
    }
    interface ParserRule {
      name?: string;
      parsers: Array<ParserConfiguration>;
    };
    interface ParserConfiguration {
      name: string; // parser name: to-json, to-tailwind...
      output: 
        | { type: 'directory', directoryPath: string }
        | { type: 'file', filePath: string };
      options?: Record<string, any>; // depends on the selected parser
    }
    type SDTFDataBox = {
      type: "SDTF";
      graph: SpecifyDesignTokenFormat;
      metadata?:
        | {
            activeViewName: string | null;
            views: Array<{
              name: string;
              query: SDTFQuery;
            }>;
          }
        | undefined;
    };
    type SDTFEngineDataBox = {
        type: "SDTF Engine";
        engine: SDTFEngine;
    }
    type SpecifyRepositoryDataBox = {
        type: "repository";
        owner: string;
        name: string;
    }
    type JSONDataBox = {
        type: "JSON";
        json: string | number | boolean | unknown[] | Record<string, unknown> | null;
    }
    type VectorDataBox = {
        type: 'vector';
        format: 'svg' | 'pdf';
        provider: 'external' | 'Specify';
        extension: {
          vector?: string, // The text representation of the SVG file
        },
    }
    type BitmapDataBox = {
        type: 'bitmap';
        format: 'png' | 'wp2' | 'avif' | 'webp' | 'jpg' | 'jxl';
        provider: 'external' | 'Specify';
        extension: {
          bitmap?: Array<number>, // The array buffer representation of the bitmap file
        },
    }
    type AssetDataBox = {
        type: 'asset';
        format: 'png' | 'wp2' | 'avif' | 'webp' | 'jpg' | 'jxl' | 'svg' | 'pdf' | 'ttf' | 'woff' | 'woff2' | 'otf' | 'eot';
        provider: 'external' | 'Specify' | 'Google Fonts' | 'Adobe Fonts';
    }
    type FileOutput = {
      type: 'file';
      filePath: string;
    }
    "parsers": [
      {
        "name": "to-css-custom-properties",
        "output": {
          "type": "file",
          "filePath": "style.css"
        }
      }
    ]
    type DirectoryOutput = {
      type: 'directory';
      directoryPath: string;
    }
    "parsers": [
      {
        "name": "to-svg-file",
        "output": {
          "type": "directory",
          "directoryPath": "./public/assets"
        }
      }
    ]
    interface parser {
      name: 'register-view';
      options: {
        name: string;
        query: SDTFQuery;
      };
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Generate tokens",
          "parsers": [
            {
              "name": "to-scss-map",
              "output": {
                "type": "directory",
                "directoryPath": "public"
              },
              "options": {}
            }
          ]
        }
      ]
    }
    public/_colors.scss
    @use "sass:map";
    
    $color: (
      colors-core-blue-100-dark: rgb(41, 52, 67),
      colors-core-blue-100-light: rgb(219, 236, 254),
      colors-core-blue-700-dark: rgb(96, 168, 250),
      colors-core-blue-700-light: rgb(17, 125, 249),
      colors-semantic-background-button-primary-hover-dark: rgb(41, 52, 67),
      colors-semantic-background-button-primary-hover-light: rgb(17, 125, 249),
    );
    
    @mixin get-color($levels...) {
      $fetched: $color;
      @each $level in $levels {
        @if map-has-key($fetched, $level) {
          $fetched: map-get($fetched, $level);
        } @else {
          @error "There is no `#{$level}` in the `#{$color}` map";
        }
      }
      @if type-of($fetched) != map {
        @error "Non usable value. Got `#{$color}`";
      }
    
      @each $prop, $value in $fetched {
        #{$prop}: $value;
      }
    }
    interface parser {
      name: 'to-flutter';
      output: {
        type: 'file';
        filePath: string;
      };
      options?:{
        tokenNameTemplate?: string;
      }
    }
    interface parser {
      name: 'to-swift';
      output: {
        type: 'file';
        filePath: string;
      };
      options?: {
        tokenNameTemplate?: string;
        scopeName?: string;
      }
    }
    interface parser {
      name: 'to-style-dictionary';
      output: {
        type: 'directory';
        directoryPath: string;
      };
    }
    interface parser {
      name: 'to-css-font-import';
      output: {
        type: 'file';
        filePath: string;
      };
      options?: {
        formats?: Array<'woff' | 'woff2' | 'ttf' | 'otf' | 'eot'>;
        fontsPath?: string;
        includeFontWeight?: boolean;
        genericFamily?: string;
        fontDisplay?: 'auto' | 'block' | 'swap' | 'fallback' | 'optional';
      };
    }
    "
    ]
    },

    Array<BuiltInParserRuleConfiguration>

    true

    The parsers you want to apply to transform your token tree. For further details see Parsers.

    false

    The options relative to the parser you apply. Each parser has its own options. For further details see Parsers.

    output

    object

    false

    The output you want the parser to generate. In most cases, a file or a directory.

    all available parsers
    "$collection": {
    "$modes": ["light", "dark"]
    },
    "info": {
    "infoToken": {
    "$type": "color",
    "$value": {
    "light": {
    "model": "rgb",
    "red": 219,
    "green": 234,
    "blue": 254,
    "alpha": 1
    },
    "dark": {
    "model": "rgb",
    "red": 219,
    "green": 234,
    "blue": 254,
    "alpha": 1
    }
    }
    }
    },
    "danger": {
    "dangerToken": {
    "$type": "color",
    "$value": {
    "light": {
    "model": "rgb",
    "red": 209,
    "green": 204,
    "blue": 204,
    "alpha": 1
    },
    "dark": {
    "model": "rgb",
    "red": 19,
    "green": 34,
    "blue": 54,
    "alpha": 1
    }
    }
    }
    }
    }
    }

    query

    true

    SDTFQuery

    The query that select items in the token tree.

    tokenNameTemplate

    false

    string

    {{path}}{{token}}{{mode}}

    The template the parser follows to name your tokens. You can use the path of your tokens, their token name, and their respective mode.

    {
      "colors": {
        "$collection": {
          "$modes": [
            "light",
            "dark"
          ]
        },
        "aliases": {
          "border": {
            "active": {
              "$type": "color",
              "$value": {
                "dark": {
                  "$alias": "colors.core.label.blue-base",
                  "$mode": "dark"
                },
                "light": {
                  "$alias": "colors.core.label.blue-base",
                  "$mode": "light"
                }
              }
            }
          }
        },
        "core": {
          "label": {
            "blue-base": {
              "$type": "color",
              "$value": {
                "dark": {
                  "model": "rgb",
                  "red": 96,
                  "green": 168,
                  "blue": 250,
                  "alpha": 1
                },
                "light": {
                  "model": "rgb",
                  "red": 17,
                  "green": 125,
                  "blue": 249,
                  "alpha": 1
                }
              }
            },
            "blue-lighter": {
              "$type": "color",
              "$value": {
                "dark": {
                  "model": "rgb",
                  "red": 41,
                  "green": 52,
                  "blue": 67,
                  "alpha": 1
                },
                "light": {
                  "model": "rgb",
                  "red": 219,
                  "green": 236,
                  "blue": 254,
                  "alpha": 1
                }
              }
            }
          }
        }
      },
      "dimensions": {
        "$collection": {
          "$modes": [
            "desktop",
            "mobile"
          ]
        },
        "base": {
          "dimension-01": {
            "$type": "dimension",
            "$value": {
              "mobile": {
                "value": 2,
                "unit": "px"
              },
              "desktop": {
                "value": 4,
                "unit": "px"
              }
            }
          },
          "dimension-02": {
            "$type": "dimension",
            "$value": {
              "mobile": {
                "value": 4,
                "unit": "px"
              },
              "desktop": {
                "value": 8,
                "unit": "px"
              }
            }
          }
        }
      }
    }

    false

    string

    DesignTokens

    The name of the parent class which contains all classes for all your token types.

    tokenNameTemplate

    false

    string

    {{path}}{{token}}{{mode}}

    The template the parser follows to name your tokens. You can use the path of your tokens, their token name, and their respective mode.

    scopeName

    {
      "colors": {
        "$collection": {
          "$modes": [
            "light",
            "dark"
          ]
        },
        "Core": {
          "blue-100": {
            "$type": "color",
            "$value": {
              "dark": {
                "red": 229,
                "blue": 29,
                "alpha": 1,
                "green": 29,
                "model": "rgb"
              },
              "light": {
                "red": 255,
                "blue": 255,
                "alpha": 1,
                "green": 255,
                "model": "rgb"
              }
            },
            "$description": "token 1 aliased with n modes within collection within n groups"
          },
          "blue-700": {
            "$type": "color",
            "$value": {
              "dark": {
                "red": 229,
                "blue": 0,
                "alpha": 1,
                "green": 0,
                "model": "rgb"
              },
              "light": {
                "red": 255,
                "blue": 255,
                "alpha": 1,
                "green": 200,
                "model": "rgb"
              }
            },
            "$description": "token 2 aliased with n modes within collection within n groups"
          }
        },
        "semantic": {
          "background": {
            "button": {
              "primary": {
                "hover": {
                  "$type": "color",
                  "$value": {
                    "dark": {
                      "$mode": "dark",
                      "$alias": "colors.Core.blue-100"
                    },
                    "light": {
                      "$mode": "light",
                      "$alias": "colors.Core.blue-700"
                    }
                  },
                  "$description": "alias token with n modes within collection within n groups"
                }
              }
            }
          }
        }
      }
    }
    {
      "colors": {
        "$collection": {
          "$modes": [
            "light",
            "dark"
          ]
        },
        "Core": {
          "blue-100": {
            "$type": "color",
            "$value": {
              "dark": {
                "red": 229,
                "blue": 29,
                "alpha": 1,
                "green": 29,
                "model": "rgb"
              },
              "light": {
                "red": 255,
                "blue": 255,
                "alpha": 1,
                "green": 255,
                "model": "rgb"
              }
            },
            "$description": "token 1 aliased with n modes within collection within n groups"
          },
          "blue-700": {
            "$type": "color",
            "$value": {
              "dark": {
                "red": 229,
                "blue": 0,
                "alpha": 1,
                "green": 0,
                "model": "rgb"
              },
              "light": {
                "red": 255,
                "blue": 255,
                "alpha": 1,
                "green": 200,
                "model": "rgb"
              }
            },
            "$description": "token 2 aliased with n modes within collection within n groups"
          }
        },
        "semantic": {
          "background": {
            "button": {
              "primary": {
                "hover": {
                  "$type": "color",
                  "$value": {
                    "dark": {
                      "$mode": "dark",
                      "$alias": "colors.Core.blue-100"
                    },
                    "light": {
                      "$mode": "light",
                      "$alias": "colors.Core.blue-700"
                    }
                  },
                  "$description": "alias token with n modes within collection within n groups"
                }
              }
            }
          }
        }
      }
    }
    {
      "colors": {
        "$collection": {
          "$modes": [
            "light",
            "dark"
          ]
        },
        "Core": {
          "blue-100": {
            "$type": "color",
            "$value": {
              "dark": {
                "red": 229,
                "blue": 29,
                "alpha": 1,
                "green": 29,
                "model": "rgb"
              },
              "light": {
                "red": 255,
                "blue": 255,
                "alpha": 1,
                "green": 255,
                "model": "rgb"
              }
            },
            "$description": "token 1 aliased with n modes within collection within n groups"
          },
          "blue-700": {
            "$type": "color",
            "$value": {
              "dark": {
                "red": 229,
                "blue": 0,
                "alpha": 1,
                "green": 0,
                "model": "rgb"
              },
              "light": {
                "red": 255,
                "blue": 255,
                "alpha": 1,
                "green": 200,
                "model": "rgb"
              }
            },
            "$description": "token 2 aliased with n modes within collection within n groups"
          }
        },
        "semantic": {
          "background": {
            "button": {
              "primary": {
                "hover": {
                  "$type": "color",
                  "$value": {
                    "dark": {
                      "$mode": "dark",
                      "$alias": "colors.Core.blue-100"
                    },
                    "light": {
                      "$mode": "light",
                      "$alias": "colors.Core.blue-700"
                    }
                  },
                  "$description": "alias token with n modes within collection within n groups"
                }
              }
            }
          }
        }
      }
    }
    The next folder level is the name of the potential first group containing the token → {collectionName?}/{SDCategory}/{1stLevelGroupName?}
  • The default filename is the name of the first group, or the name of the each mode the token might have, or base.json → {collectionName?}/{SDCategory}/{ mode? | 1stLevelGroupName? | base}.json (priority order for filename: groupName > mode > base)

  • The token path inside the file must match the token file path with the following priority order: collection > SDCategoryType > Mode > Groups

    • {collectionName?}/{SDCategory}/{1stLevelGroupName? | mode? | base}.json → { collection: {type: {mode: { groupName: { tokenName: ... }}}}

  • false

    string

    The path of font's directory.

    includeFontWeight

    false

    boolean

    Allow to include the font-weight property in the result.

    genericFamily

    false

    string

    The generic font family will be applied after the main font family.

    fontDisplay

    false

    string

    swap

    How your font face is displayed based on whether and when it is downloaded and ready to use.

    formats

    false

    Array<string>

    ['woff', 'woff2']

    The list of formats to import.

    fontsPath

    SDTFQuery
    SDTFQuery

    base lets you set the branch your PR will be merged on

  • rules lets you transform tokens by chaining parsers

  • circle-info

    Make sure you have connected your GitHub account with your Specify account. Head toward this articlearrow-up-right to learn more.

    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Generate tokens as a React Native theme",
          "parsers": [
            {
              "name": "convert-dimension",
              "options": {
                "toFormat": null
              }
            },
            {
              "name": "to-react-native",
              "output": {
                "type": "file",
                "filePath": "theme.js"
              }
            }
          ]
        },
        {
          "name": "Optimize and transform vectors with svgo + Generate JSX components",
          "parsers": [
            {
              "name": "svgo",
              "options": {
                "svgo": {
                  "plugins": [
                    {
                      "name": "removeDimensions"
                    },
                    {
                      "name": "convertColors",
                      "params": {
                        "currentColor": true
                      }
                    }
                  ]
                }
              }
            },
            {
              "name": "svg-to-jsx",
              "output": {
                "type": "directory",
                "directoryPath": "output/assets"
              }
            }
          ]
        }
      ]
    }
    hashtag
    What's a converter?

    A converter is a function that takes a resolvable alias strategy, an unresolvable alias strategy, and returns a function that takes a TokenState and will convert it to any available format. This is the way to build your custom output with the SDK. We provide you the conversion, and you only have to format it the way you want. Here is an example:

    This is as straightforward as it looks, but you're probably wondering what is the strategy const.

    hashtag
    The alias strategies

    Let's focus on what are the strategies. When working with the SDTF, you are probably aware that a token can contains an alias at a top level, mode level, or value level. When converting a token, we need to decide what we want to do with the aliases. In the case of resolvable aliases, there will be generally 2 strategies:

    • Resolve the alias to retrieve the value and use the value as is

    • If the target language supports it, convert the alias itself to a variable, alias, etc... Although it may sounds simple, the reality is that there's a lot of pitfalls, especially when trying to work with the aliases without resolving them. This is the reason why we provide premade strategies, so you don't have to go through all the pain of implementing it.

    Note that there is strategies both for resolvable and unresolvable aliases. Generally speaking, the recommended way to work with a token is the following:

    The most important here are the first lines. If the token is not fully resolvable, we just ignore it, and you should do it this way as much as possible. Working with unresolvable aliases is not really desirable, so it's better to just avoid doing it. That's why the default unresolvable alias strategy is to throw an error, and checking if the token is fully resolvable or not is enough to make sure you'll avoid a lot of problems.

    hashtag
    Using a strategy

    Let's take CSS as example, and compare the output of various strategies.

    hashtag
    Resolve alias strategy

    This strategy will resolve the token in order to retrieve the value and convert it to CSS:

    hashtag
    Alias to var strategy

    Because CSS supports the aliasing through the var(...) notation, we can rely on it to convert our aliases to a CSS variable:

    hashtag
    Throw on unresolvable strategy

    This strategy is the default one for the unresolvable aliases. As the name is saying it, It'll throw an error if we encounter an unresolvable alias during the conversion:

    Note that this strategy is the default one, so you don't need to pass anything.

    hashtag
    Ignore unresolvable strategy

    This strategy is ignoring all the unresolvable aliases if it finds one, which mean that it'll return undefined instead.

    hashtag
    Unresolvable alias to variable

    This strategy is actually quite special. It'll convert unresolvable aliases to a CSS variable. Even if an alias is unresolvable, we still have a lot of informations on the target based on the path, mode, and the token referencing the alias.

    Note that top level aliases lacks to much informations, so if this strategy encounter one, it'll throw an error.

    hashtag
    Create your own strategy

    If the provided strategies don't satisfy you, you can still decide to create your own strategy! In the case of a resolvable alias strategy, you'll have to create a function that follows this type:

    Let's breakdown the signature:

    • Return is the desired return type for your strategy. Generally it'll be the same than the converters

    • Composites is a union of the types of the composites tokens. Those tokens are expected to be converted to a Record<string, Return> as they have multiple outputs

    • About the Alias, it's a generic that will be one of these:

    Finally, you can notice that in the case of a top level alias, you need to return an object containing the modes, and then the return value.

    Here is an example of the CSS resolvable alias strategy:

    We expect the return type to be a string, and the tokens font, textStyle and transition to return multiple values, thus, a Record<string, string>.

    In general, you'll probably don't want to work with ResolvableAliasStrategy, but rather with the converter strategy, e.g: CssResolvableAliasStrategy. Here is an example of an implementation:

    hashtag
    The output of a converter

    hashtag
    Primitives tokens

    Primitives tokens are generally quite straightforward to convert to a different output. So almost every time, the output of a primitive token will be a single output. Here is an example with a dimension token converted to CSS:

    Input:

    Output:

    hashtag
    Composites tokens

    Composites tokens are a bit more complex than the simples ones. The main issue is that they contain a lot of informations, and most of the time we need multiple outputs to convert all the data. To do so, a converted composites token will output an object containing all the outputs. Here is an example with a transition token that we convert to a CSS output.

    Input:

    Output:

    In the case of a transition, we can't assume on which property the transition will be applied, so we have to split everything in different tokens so you can build your transition on the property that you want.

    If you want more informations on which tokens are primitives and which ones are composites, and on the inputs and outputs, you can have a look to the reference of each converters:

    • CSS

    With your SDTF client, you can create as many parsers pipelines as you need to generate your outputs.

    The executePipelines is an asynchronous function that will actually execute the generation. Hence:

    The results is a ParsersEngineResults instance, which comes with its own set of helper methods to work with the outputs and messages issued over the generation.

    circle-info

    Note that results is plural since it can accumulate more than one parsers pipeline for a single execution.

    hashtag
    Write the outputs to the file system

    While being executed, the parsers engine produces outputs that gets returned within the ParsersEngineResults instance which comes with few helper methods like writeToDisk

    writeToDisk takes an optional base path and returns a promise containing a report of the written files and errors if any.

    hashtag
    Run many concurrent parsers pipelines

    All parsers pipelines passed to createParsersPipelines are run concurrently out of the box. It means, you can write the following:

    And have both pipelines executed concurrently from the same initial token tree.

    hashtag
    Chain parsers to run specific pre-generation

    In some cases, you need to chain the parser functions to act like A -> B -> C where you are only interested in C. For that matter, you can leverage the chainParserFunctions util.

    In this example, we want to optimize the content of the vector tokens with SVGO, and then, generate JSX components.

    hashtag
    Execute a built-in parser function remotely

    In some cases, you might need to deal with few host machine resources (like in many CI). To help with this, any Specify's built-in generation parser can be executed remotely by passing the shouldExecuteRemotely: true option.

    Doing so, the SVGO process will be run on Specify's servers and the results will be returned to the SDK to be further processed / written on disk.

    hashtag
    Create pipelines from parser rules configuration

    A Parser rule is a JSON object representing a parsers pipeline where all parsers will be run sequencially. Rules configuration are primarily utilized within the configuration file for the CLI or GitHub.

    With the SDK, the use of parser rules configuration reduces the interoperability with custom code, but can significantly increase the speed of a remote execution.

    In order to build parsers pipelines from the SDTF client we need to call the createParsersPipelinesFromRules method.

    Doing so, it creates an async parsers engine executor in the exact same manner it did for parser functions.

    hashtag
    Run faster remote executions

    Any built-in generation parser holds a shouldExecuteRemotely: boolean option to treat its inner execution as remote.

    Yet, rules configuration also implement that option, allowing the SDK to collect all the remote rules and parsers, then sends out a single HTTP request for the whole execution.

    hashtag
    Create your custom parser function

    If the parsers that Specify is providing are not enough for your use case, you can create your own parser function!

    Now that we are able to execute parsers locally, it means that parsers are simple functions, so creating a custom parser is only about writing a function. But before creating your own parser, you have to understand how a parser is working.

    circle-info

    The next part will describe how parsers are working, but you'll quickly notice that it doesn't looks like the parsers above, e.g: an output option and a parser option. The reason for it is that all our parsers are actually functions that return a parser function. So don't worry if it doesn't looks like above, in the end it's all the same thing

    hashtag
    The anatomy of a parser

    A parser is a function that will take 3 parameters:

    1. An input will be one of the type of ParsersEngineDataBox :

      1. SDTFEngineDataBox: { type: 'SDTF Engine'; engine: SDTFEngine; }

      2. SDTFDataBox: { type: 'SDTF'; graph: SpecifyDesignTokenFormat; }

      3. JSONDataBox: { type: 'JSON'; json: Record<string, unknown>; }

      4. SVGDataBox: { type: 'SVG'; svg: Array<{ ... }> }

      5. UrlDataBox: { type: 'urls'; files: Array<{...}> }

      6. BitmapDataBox: { type: 'bitmap'; files: Array<{...}> }

      7. CustomDataBox: { type: 'custom'; custom: unknown }

    2. The ParserToolbox, which helps accumulate the output that will be written to your file system

    An important thing to understand is that a parser has 2 outputs:

    1. The return type of the function, that can be passed to another parser if chained

    2. The output that you want to write to the file system (files, text, JSON, SDTF), and that'll be accumulated into the ParserToolbox

    There's actually 2 reasons for this choice:

    1. There's only 1 return value, but you can append as much output as you want to an accumulator

    2. We need a difference between the output of a parser, and what we want to send to the next parser

    Let's have a look to the output in itself.

    hashtag
    The parser output

    First, let's focus on the return. The output will be the input of the next parser if you use it inside a ParserChainer. So as the return is the input of the next parser, you probably guessed it: it's the same one for the input, which means one of ParsersEngineDataBox :

    • SDTFEngineDataBox: { type: 'SDTF Engine'; engine: SDTFEngine; }

    • SDTFDataBox: { type: 'SDTF'; graph: SpecifyDesignTokenFormat; }

    • JSONDataBox: { type: 'JSON'; json: Record<string, unknown>; }

    • SVGDataBox: { type: 'SVG'; svg: Array<{ ... }> }

    • BitmapDataBox: { type: 'bitmap'; files: Array<{...}> }

    • UrlDataBox: { type: 'urls'; files: Array<{...}> }

    • CustomDataBox: { type: 'custom'; custom: unknown }

    Now, let's see how we can output files. To do so, we need to push into the outputsAccumulator one of the types of the ParserOutput:

    • TextOutput: { type: 'text'; text: string }

    • SDTFOutput: { type: 'SDTF'; graph: SpecifyDesignTokenFormat }

    • JSONOutput: { type: 'JSON'; graph: string }

    • FilesOutput: { type: 'files'; files: Array<{ path: string; content: { type: 'text'; text: string; } | { type: 'url'; url: string; }}> }

    Most of the parsers take an SDTFDataBox as an input, and return it as the output as they don't modify anything and only output some files. So if you're not sure about what to return, just return the input.

    So now that we know what is a parser, let's have a look to an example of parser that create a file with all the token's name:

    Let's break down the example:

    1. We use the engine to get all the names

    1. We populate the output into the accumulator

    1. Finally, we return the input as we didn't modify anything and don't need to return something else

    Now that we have our custom parser, we can use it freely in the ParserPipeline or ParserChainer.

    SDTF client
    SDK
    parsers
    Record<string, any>
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Only get tokens from a group named 'info' and gererate tokens in JSON",
          "parsers": [
            {
              "name": "register-view",
              "options": {
                "name": "Info only",
                "query": {
                  "where": {
                    "group": "info",
                    "select": {
                      "parents": true,
                      "children": true
                    }
                  }
                }
              }
            },
            {
              "name": "to-css-custom-properties",
              "options": {
                "withSDTFView": "Info only"
              },
              "output": {
                "type": "file",
                "filePath": "tokens.css"
              }
            }
          ]
        }
      ]
    }
    tokens.css
    :root[data-colors="light"] {
      --info-infoToken: rgb(219, 234, 254);
    }
    :root[data-colors="dark"] {
      --info-infoToken: rgb(219, 234, 254);
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "To Flutter",
          "parsers": [
            {
              "name": "to-flutter",
              "output": {
                "type": "file",
                "filePath": "public/tokens.dart"
              }
            }
          ]
        }
      ]
    }
    output/assets/icons/menu.tsx
    // ignore_for_file: camel_case_types
    
    import 'dart:core';
    import 'package:flutter/painting.dart';
    import 'package:flutter/animation.dart';
    
    class Color_ {
      static const colorsCoreLabelBlueBaseDark = Color.fromRGBO(96, 250, 168, 1.0);
      static const colorsCoreLabelBlueBaseLight = Color.fromRGBO(17, 249, 125, 1.0);
      static const colorsAliasesBorderActiveDark = Color.fromRGBO(96, 250, 168, 1.0);
      static const colorsAliasesBorderActiveLight = Color.fromRGBO(17, 249, 125, 1.0);
      static const colorsCoreLabelBlueLighterDark = Color.fromRGBO(41, 67, 52, 1.0);
      static const colorsCoreLabelBlueLighterLight = Color.fromRGBO(219, 254, 236, 1.0);
    }
    
    class Dimension_ {
      static const dimensionsBaseDimension_01Desktop = 4;
      static const dimensionsBaseDimension_01Mobile = 2;
      static const dimensionsBaseDimension_02Desktop = 8;
      static const dimensionsBaseDimension_02Mobile = 4;
    }
    {
      "colors": {
        "$collection": {
          "$modes": [
            "light",
            "dark"
          ]
        },
        "aliases": {
          "border": {
            "active": {
              "$type": "color",
              "$value": {
                "dark": {
                  "$alias": "colors.core.label.blue-base",
                  "$mode": "dark"
                },
                "light": {
                  "$alias": "colors.core.label.blue-base",
                  "$mode": "light"
                }
              }
            }
          }
        },
        "core": {
          "label": {
            "blue-base": {
              "$type": "color",
              "$value": {
                "dark": {
                  "model": "rgb",
                  "red": 96,
                  "green": 168,
                  "blue": 250,
                  "alpha": 1
                },
                "light": {
                  "model": "rgb",
                  "red": 17,
                  "green": 125,
                  "blue": 249,
                  "alpha": 1
                }
              }
            },
            "blue-lighter": {
              "$type": "color",
              "$value": {
                "dark": {
                  "model": "rgb",
                  "red": 41,
                  "green": 52,
                  "blue": 67,
                  "alpha": 1
                },
                "light": {
                  "model": "rgb",
                  "red": 219,
                  "green": 236,
                  "blue": 254,
                  "alpha": 1
                }
              }
            }
          }
        }
      },
      "dimensions": {
        "$collection": {
          "$modes": [
            "desktop",
            "mobile"
          ]
        },
        "base": {
          "dimension-01": {
            "$type": "dimension",
            "$value": {
              "mobile": {
                "value": 2,
                "unit": "px"
              },
              "desktop": {
                "value": 4,
                "unit": "px"
              }
            }
          },
          "dimension-02": {
            "$type": "dimension",
            "$value": {
              "mobile": {
                "value": 4,
                "unit": "px"
              },
              "desktop": {
                "value": 8,
                "unit": "px"
              }
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "To Swift",
          "parsers": [
            {
              "name": "to-swift",
              "output": {
                "type": "file",
                "filePath": "public/tokens.swift"
              }
            }
          ]
        }
      ]
    }
    output/tokens.swift
    import SwiftUI
    
    class DesignTokens {
      class Color_ {
        static let colorsCoreLabelBlue_baseDark = Color(red: 0.37647, green: 0.65882, blue: 0.98039, opacity: 1)
        static let colorsCoreLabelBlue_baseLight = Color(red: 0.06667, green: 0.49020, blue: 0.97647, opacity: 1)
        static let colorsAliasesBorderActiveDark = Color(red: 0.37647, green: 0.65882, blue: 0.98039, opacity: 1)
        static let colorsAliasesBorderActiveLight = Color(red: 0.06667, green: 0.49020, blue: 0.97647, opacity: 1)
        static let colorsCoreLabelBlue_lighterDark = Color(red: 0.16078, green: 0.20392, blue: 0.26275, opacity: 1)
        static let colorsCoreLabelBlue_lighterLight = Color(red: 0.85882, green: 0.92549, blue: 0.99608, opacity: 1)
      }
    
      class Dimension {
        static let dimensionsBaseDimension_01Desktop = 4
        static let dimensionsBaseDimension_01Mobile = 2
        static let dimensionsBaseDimension_02Desktop = 8
        static let dimensionsBaseDimension_02Mobile = 4
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "To JavaScript",
          "parsers": [
            {
              "name": "to-javascript",
              "output": {
                "type": "file",
                "filePath": "tokens.js"
              }
            }
          ]
        }
      ]
    }
    tokens.js
    /** 
    * @enum {string} All the valid paths for the collection colors.
    * Use it when calling `getColorsTokenByMode`
    */
    export const colorsColorPath = {
      'colors.Core.blue-100': 'colors.Core.blue-100',
      'colors.Core.blue-700': 'colors.Core.blue-700',
      'colors.semantic.background.button.primary.hover': 'colors.semantic.background.button.primary.hover'
    };
    
    /**
    * All the modes of the collection colors.
    * Use it when calling `getColorsTokenByMode`
    */
    export const colorsModes = [ 'light', 'dark' ];
    
    /** 
    * All the tokens of the collection colors.
    * Use `getColorsTokenByMode` to retrieve the tokens
    */
    export const colors = {
      'colors.Core.blue-100': { dark: 'rgb(229, 29, 29)', light: 'rgb(255, 255, 255)' },
      'colors.Core.blue-700': { dark: 'rgb(229, 0, 0)', light: 'rgb(255, 200, 255)' },
      'colors.semantic.background.button.primary.hover': { dark: 'rgb(229, 29, 29)', light: 'rgb(255, 200, 255)' }
    };
    
    /**
    * Retrieve a token for the collection 'colors'.
    * @param {keyof typeof colorsPath} path - The path to the token
    * @param {'light' | 'dark'} mode - The mode of the token you want to retrieve 
    * @returns {number | string} The value of a token for a given mode
    */
    export function getColorsTokenByMode(path, mode) {
      if (!colors[path]) {
        throw new Error("Path: '" + path + "' doesn't exist for collection 'colors'. Here are all the valid paths for each type:" + `
    - color:
        - colors.Core.blue-100
        - colors.Core.blue-700
        - colors.semantic.background.button.primary.hover`)
      }
    
      if (!colors[path][mode]) {
        throw new Error("Invalid mode '" + mode.toString() + "' for collection 'colors' at path '" + path + "', here are all the valid modes:\n- " + Object.keys(colors[path]).join('\n- '))
      }
    
      return colors[path][mode]
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Generate tokens in JSON",
          "parsers": [
            {
              "name": "to-sdtf",
              "output": {
                "type": "file",
                "filePath": "output/tokens.json"
              }
            }
          ]
        }
      ]
    }
    tokens.json
    {
      "colors": {
        "$collection": {
          "$modes": [
            "light",
            "dark"
          ]
        },
        "Core": {
          "blue-100": {
            "$type": "color",
            "$value": {
              "dark": {
                "red": 229,
                "blue": 29,
                "alpha": 1,
                "green": 29,
                "model": "rgb"
              },
              "light": {
                "red": 255,
                "blue": 255,
                "alpha": 1,
                "green": 255,
                "model": "rgb"
              }
            },
            "$description": "token 1 aliased with n modes within collection within n groups"
          },
          "blue-700": {
            "$type": "color",
            "$value": {
              "dark": {
                "red": 229,
                "blue": 0,
                "alpha": 1,
                "green": 0,
                "model": "rgb"
              },
              "light": {
                "red": 255,
                "blue": 255,
                "alpha": 1,
                "green": 200,
                "model": "rgb"
              }
            },
            "$description": "token 2 aliased with n modes within collection within n groups"
          }
        },
        "semantic": {
          "background": {
            "button": {
              "primary": {
                "hover": {
                  "$type": "color",
                  "$value": {
                    "dark": {
                      "$mode": "dark",
                      "$alias": "colors.Core.blue-100"
                    },
                    "light": {
                      "$mode": "light",
                      "$alias": "colors.Core.blue-700"
                    }
                  },
                  "$description": "alias token with n modes within collection within n groups"
                }
              }
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "To TypeScript",
          "parsers": [
            {
              "name": "to-typescript",
              "output": {
                "type": "file",
                "filePath": "tokens.ts"
              }
            }
          ]
        }
      ]
    }
    tokens.js
    /** 
    * @enum {string} All the valid paths for the collection colors.
    * Use it when calling `getColorsTokenByMode`
    */
    export const colorsColorPath = {
      'colors.Core.blue-100': 'colors.Core.blue-100',
      'colors.Core.blue-700': 'colors.Core.blue-700',
      'colors.semantic.background.button.primary.hover': 'colors.semantic.background.button.primary.hover'
    } as const;
    
    export type colorsColorPathType = keyof typeof colorsColorPath;
    
    export type colorsPathType = colorsColorPathType;
    
    /**
    * All the modes of the collection colors.
    * Use it when calling `getColorsTokenByMode`
    */
    export const colorsModes = [ 'light', 'dark' ] as const;
    
    export type colorsModesType = typeof colorsModes[number];
    
    /** 
    * All the tokens of the collection colors.
    * Use `getColorsTokenByMode` to retrieve the tokens
    */
    export const colors = {
      'colors.Core.blue-100': { dark: 'rgb(229, 29, 29)', light: 'rgb(255, 255, 255)' },
      'colors.Core.blue-700': { dark: 'rgb(229, 0, 0)', light: 'rgb(255, 200, 255)' },
      'colors.semantic.background.button.primary.hover': { dark: 'rgb(229, 29, 29)', light: 'rgb(255, 200, 255)' }
    } as const;
    
    /**
    * Retrieve a token for the collection 'colors'.
    * @param {keyof typeof colorsPath} path - The path to the token
    * @param {'light' | 'dark'} mode - The mode of the token you want to retrieve 
    * @returns {number | string} The value of a token for a given mode
    */
    export function getColorsTokenByMode<T extends colorsPathType, M extends keyof typeof colors[T]>(path: T, mode: M) {
      if (!colors[path]) {
        throw new Error("Path: '" + path + "' doesn't exist for collection 'colors'. Here are all the valid paths for each type:" + `
    - color:
        - colors.Core.blue-100
        - colors.Core.blue-700
        - colors.semantic.background.button.primary.hover`)
      }
    
      if (!colors[path][mode]) {
        throw new Error("Invalid mode '" + mode.toString() + "' for collection 'colors' at path '" + path + "', here are all the valid modes:\n- " + Object.keys(colors[path]).join('\n- '))
      }
    
      return colors[path][mode]
    }
    {
      "colors": {
        "$collection": { "$modes": ["light", "dark"] },
        "core": {
          "blue-100": {
            "$type": "color",
            "$description": "token 1 aliased with n modes within collection within n groups",
            "$value": {
              "light": {
                "red": 255,
                "blue": 255,
                "alpha": 1,
                "green": 255,
                "model": "rgb"
              },
              "dark": {
                "red": 229,
                "blue": 29,
                "alpha": 1,
                "green": 29,
                "model": "rgb"
              }
            }
          },
          "blue-700": {
            "$type": "color",
            "$description": "token 2 aliased with n modes within collection within n groups",
            "$value": {
              "light": {
                "red": 255,
                "blue": 255,
                "alpha": 1,
                "green": 200,
                "model": "rgb"
              },
              "dark": {
                "red": 229,
                "blue": 0,
                "alpha": 1,
                "green": 0,
                "model": "rgb"
              }
            }
          }
        },
        "semantic": {
          "background": {
            "button": {
              "primary": {
                "hover": {
                  "$type": "color",
                  "$description": "alias token with n modes within collection within n groups",
                  "$value": {
                    "dark": {
                      "$mode": "dark",
                      "$alias": "colors.core.blue-100"
                    },
                    "light": {
                      "$mode": "light",
                      "$alias": "colors.core.blue-700"
                    }
                  }
                }
              }
            }
          }
        }  
      }
    }
    
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Generate Style Dictionary raw token files",
          "parsers": [
            {
              "name": "to-style-dictionary",
              "output": {
                "type": "directory",
                "directoryPath": "output/tokens"
              }
            }
          ]
        }
      ]
    }
    color/light/core.json
    {
      "colors": {
        "color": {
          "light": {
            "core": {
              "blue-100": {
                "value": "rgb(255, 255, 255)",
                "type": "color",
                "description": "token 1 aliased with n modes within collection within n groups"
              },
              "blue-700": {
                "value": "rgb(255, 200, 255)",
                "type": "color",
                "description": "token 2 aliased with n modes within collection within n groups"
              }
            }
          }
        }
      }
    }
    color/dark/core.json
    {
      "colors": {
        "color": {
          "dark": {
            "core": {
              "blue-100": {
                "value": "rgb(229, 29, 29)",
                "type": "color",
                "description": "token 1 aliased with n modes within collection within n groups"
              },
              "blue-700": {
                "value": "rgb(229, 0, 0)",
                "type": "color",
                "description": "token 2 aliased with n modes within collection within n groups"
              }
            }
          }
        }
      }
    }
    color/light/semantic.json
    {
      "colors": {
        "color": {
          "light": {
            "semantic": {
              "background": {
                "button": {
                  "primary": {
                    "hover": {
                      "value": "{colors.color.light.core.blue-700}",
                      "type": "color",
                      "description": "alias token with n modes within collection within n groups"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    {
      "font": {
        "interRegular": {
          "$type": "font",
          "$value": {
            "default": {
              "family": "Inter",
              "postScriptName": "Inter Regular",
              "weight": "regular",
              "style": "normal",
              "files": [
                {
                  "format": "ttf",
                  "url": "https://static.specifyapp.com/sdtf-seeds/inter-regular.ttf",
                  "provider": "Specify"
                }
              ]
            }
          }
        },
        "interMedium": {
          "$type": "font",
          "$value": {
            "default": {
              "family": "Inter",
              "postScriptName": "Inter Medium",
              "weight": "medium",
              "style": "normal",
              "files": [
                {
                  "format": "ttf",
                  "url": "https://static.specifyapp.com/sdtf-seeds/inter-medium.ttf",
                  "provider": "Specify"
                }
              ]
            }
          }
        },
        "interBold": {
          "$type": "font",
          "$value": {
            "default": {
              "family": "Inter",
              "postScriptName": "Inter Bold",
              "weight": "bold",
              "style": "normal",
              "files": [
                {
                  "format": "ttf",
                  "url": "https://static.specifyapp.com/sdtf-seeds/inter-bold.ttf",
                  "provider": "Specify"
                }
              ]
            }
          }
        },
        "firaCodeRegular": {
          "$type": "font",
          "$value": {
            "default": {
              "family": "Fira Code",
              "postScriptName": "Fira Code Regular",
              "weight": "regular",
              "style": "normal",
              "files": [
                {
                  "format": "ttf",
                  "url": "https://static.specifyapp.com/sdtf-seeds/fira-code-regular.ttf",
                  "provider": "Specify"
                }
              ]
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Generate font imports",
          "parsers": [
            {
              "name": "to-css-font-import",
              "output": {
                "type": "file",
                "filePath": "public/fonts.css"
              },
              "options": {
                "fontsPath": "assets/fonts/"
              }
            }
          ]
        }
      ]
    }
    public/fonts.css
    @font-face {
    	font-family: 'Fira Code';
    	src: url('assets/fonts/Fira Code.woff') format('woff'), url('assets/fonts/Fira Code.woff2') format('woff2');
    	font-display: swap;
    	font-weight: regular;
    }
    @font-face {
    	font-family: 'Inter';
    	src: url('assets/fonts/Inter.woff') format('woff'), url('assets/fonts/Inter.woff2') format('woff2');
    	font-display: swap;
    	font-weight: bold;
    }
    @font-face {
    	font-family: 'Inter';
    	src: url('assets/fonts/Inter.woff') format('woff'), url('assets/fonts/Inter.woff2') format('woff2');
    	font-display: swap;
    	font-weight: medium;
    }
    @font-face {
    	font-family: 'Inter';
    	src: url('assets/fonts/Inter.woff') format('woff'), url('assets/fonts/Inter.woff2') format('woff2');
    	font-display: swap;
    	font-weight: regular;
    }
    | { collection: string | true }
    | { group: string | true }
    | { token: string | true }
    | SDTFQuery
    | { collection: string | true }
    | { group: string | true }
    | { token: string | true }
    | SDTFQuery
    {
      "version": "2",
      "head": "specifyrc-json",
      "base": "main",
      "repository": "@organization/repository",
      "rules": [
        {
          "name": "Generate tokens as a React Native theme",
          "parsers": [
            {
              "name": "convert-dimension",
              "options": {
                "toFormat": null
              }
            },
            {
              "name": "to-react-native",
              "output": {
                "type": "file",
                "filePath": "theme.js"
              }
            }
          ]
        },
        {
          "name": "Optimize and transform vectors with svgo + Generate JSX components",
          "parsers": [
            {
              "name": "svgo",
              "options": {
                "svgo": {
                  "plugins": [
                    {
                      "name": "removeDimensions"
                    },
                    {
                      "name": "convertColors",
                      "params": {
                        "currentColor": true
                      }
                    }
                  ]
                }
              }
            },
            {
              "name": "svg-to-jsx",
              "output": {
                "type": "directory",
                "directoryPath": "output/assets"
              }
            }
          ]
        }
      ]
    }
    import {
      dimensionToCss,
      breakpointToCss,
      textStyleToCss,
      createResolveAliasStrategy 
    } from '@specify/sdk/css'
    
    const strategy = createResolveAliasStrategy()
    
    const output = tokenState.matchByType(
      {
        dimension: dimensionToCss(strategy),
        breakpoint: breakpointToCss(strategy),
        textStyle: textStyleToCss(strategy)
      }, 
      _ => undefined
    )
    import { dimensionToCss } from '@specify/sdk/css'
    
    if (!tokenState.isFullyResolvable) {
      return
    }
    
    const strategy = createResolveAliasStrategy()
    
    tokenState.matchByType(
      {
        dimension: dimensionToCss(strategy)
      }, 
      _ => undefined
    )
    const strategy = createResolveAliasStrategy()
    
    // Token value: [{ value: 12, unit: 'px'}, { $alias: 'mySpacing', $mode: 'default' }]
    tokenState.matchByType(
      {
        spacings: spacingsToCss(strategy) // -> 12px 24px
      }, 
      _ => undefined
    )
    const strategy = createAliasToVarStrategy({ tokenNotInCollectionNameTemplate: '--{{token}}}' })
    
    // Token value: [{ value: 12, unit: 'px'}, { $alias: 'mySpacing', $mode: 'default' }]
    tokenState.matchByType(
      {
        spacings: spacingsToCss(strategy) // -> 12px var(--mySpacing)
      }, 
      _ => undefined
    )
    const resolvableStrategy = createResolveAliasStrategy()
    const unresolvableStrategy = createThrowOnUnresolvableStrategy()
    
    // Token value: [{ value: 12, unit: 'px'}, { $alias: 'wrong.token', $mode: 'default' }]
    tokenState.matchByType(
      {
        // -> Error: 'wrong.token' is unresolvable
        spacings: spacingsToCss(resolvableStrategy, unresolvableStrategy)
      }, 
      _ => undefined
    )
    const resolvableStrategy = createResolveAliasStrategy()
    const unresolvableStrategy = createIgnoreUnresolvableStrategy()
    
    // Token value: [{ value: 12, unit: 'px'}, { $alias: 'wrong.token', $mode: 'default' }]
    tokenState.matchByType(
      {
        // -> 12px undefined
        spacings: spacingsToCss(resolvableStrategy, unresolvableStrategy)
      }, 
      _ => undefined
    )
    const resolvableStrategy = createResolveAliasStrategy()
    const unresolvableStrategy = createUnresolvableAliasToVarStrategy({
      tokenNotInCollectionNameTemplate: '--{{token}}'
    })
    
    // Token value: [{ value: 12, unit: 'px'}, { $alias: 'wrong.token', $mode: 'default' }]
    tokenState.matchByType(
      {
        // -> 12px var(--wrong-token)
        spacings: spacingsToCss(resolvableStrategy, unresolvableStrategy)
      }, 
      _ => undefined
    )
    type ResolvableAliasStrategy<
      Return, 
      Composites extends SpecifyDesignTokenTypeName
    > = <
      Alias extends AllResolvableAlias = AllResolvableAlias,
    >(
      alias: Alias,
    ) => Alias extends ResolvableTopLevelAlias
      ? {
          [mode: string]: Alias extends AllResolvableAlias<Composites>
            ? Record<string, Return>
            : Return;
        }
      : Alias extends AllResolvableAlias<Composites>
        ? Record<string, Return>
        : Return;
    type AllResolvableAlias<
      Type extends SpecifyDesignTokenTypeName = SpecifyDesignTokenTypeName,
    > =
      | ResolvableValueLevelAlias<Type>
      | ResolvableModeLevelAlias<Type>
      | ResolvableTopLevelAlias<Type>;
    
    type CssResolvableAliasStrategy = ResolvableAliasStrategy<
      string,
      'font' | 'textStyle' | 'transition'
    >;
    
    import {
      ResolvableValueLevelAlias,
      ResolvableModeLevelAlias,
      ResolvableTopLevelAlias
    } from '@specify/specify-design-token-format'
    import { CssResolvableAliasStrategy } from '@specifyapp/sdk/css'
    
    const myCustomStrategy: CssResolvableAliasStrategy = (alias) => {
      if (alias instanceof ResolvableTopLevelAlias) {
        ...
      } else if (alias instanceof ResolvableModeLevelAlias) {
        ...
      } else {
        ...
      }
    }
    
    tokenState.matchByType({
      dimension: dimensionToCss(myCustomStrategy)
    }, _ => undefined)
    {
        value: 12,
        unit: "px"
    }
    "12px"
    {
      duration: {
        value: 12,
        unit: 's',
      },
      delay: {
        value: 0,
        unit: 'ms',
      },
      timingFunction: [0.1, 0.2, 0.1, 0.4]
    }
    {
      delay: "12s",
      duration: "0ms",
      "timing-function": "cubic-bezier(0.1, 0.2, 0.1, 0.4)"
    }
    import { parsers } from "@specifyapp/sdk";
    
    const executePipelines = sdtfClient.createParsersPipelines(
      parsers.toTailwind({ output: { type: "file", filePath: "./tailwind.theme.js" } })
    );
    const results = await executePipelines();
    
    results.debug();
    // ...
    const results = await executePipelines();
    
    const report = await results.writeToDisk('./public');
    import { parsers } from "@specifyapp/sdk";
    
    const results = await sdtfClient.createParsersPipelines(
      parsers.toTailwind({ output: { type: "file", filePath: "./tailwind.theme.js" } }),
      parsers.toJsonList({ output: { type: 'file', filePath: 'tokens-list.json' } })
    )()
    import { parsers, chainParserFunctions } from "@specifyapp/sdk";
    
    const results = await sdtfClient.createParsersPipelines(
      chainParserFunctions(
        svgo({ options: { svgo: { ... }}}),
        svgToJsx({ output: { type: 'directory', directoryPath: 'icons' } })
      )
    )()
    import { parsers, chainParserFunctions } from "@specifyapp/sdk";
    
    const results = await sdtfClient.createParsersPipelines(
      svgo({
        options: {
          shouldExecuteRemotely: true,
          svgo: { ... }
        },
        output: { type: 'directory', directoryPath: 'icons' }
      }),
    )()
    const executePipelines = await sdtfClient.createParsersPipelinesFromRules({
      name: 'Icons to JSX',
      parsers: [
        {
          name: 'svgo',
          options: { svgo: { ... } },
        },
        {
          name: 'svg-to-jsx',
          output: { type: 'directory', directoryPath: 'icons' },
        },
      ],
    });
    const executePipelines = await sdtfClient.createParsersPipelinesFromRules({
      name: 'Icons to JSX',
      shouldExecuteRemotely: true,
      parsers: [ ... ],
    });
    import { SpecifyDesignTokenFormat } from '@specifyapp/specify-design-token-format'
    import { ParserToolbox, SDTFEngineDataBox } from '@specifyapp/sdk/bulk'
    
    function parserName(
      input: SDTFEngineDataBox,
      toolbox: ParserToolbox,
    ) {
      const engine = input.engine;
      const names = engine
        .query
        .getAllTokenStates()
        .map(tokenState => tokenState.name);
      
     toolbox.populateOutput(
       {
         type: 'files',
         files: [{ path: 'names.txt', content: { type: 'text', text: names.join('\n') } }] 
       }
     )
     
     return input
    }
    const names = tokenTreeClient
      .engine
      .query
      .getAllTokenStates()
      .map(tokenState => tokenState.name);
    toolbox.populateOutput(
      {
        type: 'files',
         files: [{ path: 'names.txt', content: { type: 'text', text: names.join('\n') } }] 
      }
    )
    return input
    Paste your GitLab project access token

    Paste your key in the value field

    Learn more ↗︎arrow-up-right
    JSONBin
    Learn more in the GitHub documentationarrow-up-right
    Create an access token ↗︎arrow-up-right
    Learn more in the Azure DevOps documentationarrow-up-right
    Learn more on the GitLab documentationarrow-up-right
    Head toward this pagearrow-up-right
    Video tutorial (7min) to help you sync tokens from Tokens Studio to your local git repo as CSS variables
    Parameter
    Required
    Type
    Default
    Description

    query

    required

    The query that select items in the graph.

    resolveAliases

    false

    boolean

    false

    Whether to resolve the aliases of the graph. Thus, preventing aliases to become unresolvable when their source is not included in the selected items.

    hashtag
    Basic usage: select all tokens from a group in a collection

    1. We want to get all tokens in all groups named "info"

    2. We also want to get the parent collection...

    3. ... and all children tokens within the "info" group(s)

    4. We eventually generate our transformed SDTF graph in a JSON file thanks to the to-sdtf parser.

    Parameter
    Required
    Type
    Default
    Description

    all

    false

    Select all collections, groups and tokens in your SDTF token graph.

    hashtag
    Basic usage: Rename design token by keeping only characters present after the last slash character (/).

    Parameter
    Required
    Type
    Default
    Description

    tokenNameTemplate

    false

    string

    {{path}}-{{token}}-{{mode}}

    The template the parser follows to name your tokens. You can use the path of your tokens, their token name, and their respective mode.

    hashtag
    Basic usage

    Parameter
    Required
    Type
    Default
    Description

    tokenNameTemplate

    false

    string

    {{path}}-{{token}}-{{mode}}

    The template the parser follows to name your tokens. You can use the path of your tokens, their token name, and their respective mode.

    hashtag
    Basic usage

    github.comarrow-up-right
    https://jsonbin.ioarrow-up-right
    URL providers authenticationarrow-up-right
    parser rule
    A private GitHub repository containing the tokens.json file
    Details over the tokens.json file to grab its raw file url
    A bin content with font tokens
    Specify web app - choose source step
    Specify web app - provide url step
    Specify web app - select design token format

    to-react-native

    This parser helps you pull design tokens as a theme compatible with React Native and their respective helper functions.

    hashtag
    Interface

    hashtag
    Basic usage

    {
      "colors": {
    

    to-css-custom-properties

    This parser helps you transform design tokens into CSS Custom Properties.

    hashtag
    Interface

    hashtag
    Options

    Parameter
    Required
    Type
    Default
    Description

    hashtag
    Basic usage

    A design token can have modes, be nested in groups and be part of a collection. The following use case will generate a single CSS file containing core tokens and semantic tokens.

    circle-info

    Head towards our to see how you can use this parser with others to suit a common use case when working with CSS.

    convert-color

    This parser helps you convert the color formats of color compatible tokens over a SDTF graph.

    hashtag
    Interface

    hashtag
    Options

    Parsers

    Parsers API reference

    hashtag
    Utility parsers

    Utility parsers help you mutate your token tree before generating final outputs.

    circle-info

    TokenState

    The TokenState class API reference

    The TokenState is accessible via the SDTFEngine API over methods like: getTokenState, getAllTokenStates…

    The TokenState host all the methods to work with the token value, read, update, resolve aliases…

    hashtag
    Properties

    to-kotlin

    This parser helps you generate design tokens in Kotlin.

    hashtag
    Interface

    hashtag
    Options

    {
      "colors": {
        "$collection": {
          "$modes": ["light", "dark"]
        },
        "info": {
          "infoToken": {
            "$type": "color",
            "$value": {
              "light": {
                "model": "rgb",
                "red": 219,
                "green": 234,
                "blue": 254,
                "alpha": 1
              },
              "dark": {
                "model": "rgb",
                "red": 219,
                "green": 234,
                "blue": 254,
                "alpha": 1
              }
            }
          }
        },
        "danger": {
          "dangerToken": {
            "$type": "color",
            "$value": {
              "light": {
                "model": "rgb",
                "red": 209,
                "green": 204,
                "blue": 204,
                "alpha": 1
              },
              "dark": {
                "model": "rgb",
                "red": 19,
                "green": 34,
                "blue": 54,
                "alpha": 1
              }
            }
          }
        }
      }
    }
    interface parser {
      name: 'filter';
      options: {
        query: SDTFQuery;
        resolveAliases?: boolean;
        allowUnresolvableAliases?: boolean;
        deduplicate: true | undefined;
        failOnMutate: true | undefined;
      };
    }
    {
      "colors": {
        "Colors/Black": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#000000",
              "alpha": 1
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Rename tokens",
          "parsers": [
            {
              "name": "replace-string",
              "options": {
                "token": {
                  "regex": {
                    "pattern": "(.*?)\\/",
                    "flags": "g"
                  },
                  "replaceBy": ""
                }
              }
            },
            {
              "name": "to-sdtf",
              "output": {
                "type": "file",
                "filePath": "tokens.json"
              }
            }
          ]
        }
      ]
    }
    tokens.json
    {
      "colors": {
        "Black": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#000000",
              "alpha": 1
            }
          }
        }
      }
    }
    interface parser {
      name: 'replace-string';
      options: ({
        all?: string | {
          regex: string | { pattern: string, flags?: 'g' & 'm' & 'i' },
          replaceBy: string
        }
      } | 
      {
        group?: string | {
          regex: string | { pattern: string, flags?: 'g' & 'm' & 'i' },
          replaceBy: string
        };
        collection?: string | {
          regex: string | { pattern: string, flags?: 'g' & 'm' & 'i' },
          replaceBy: string
        };
        token?: string | {
          regex: string | { pattern: string, flags?: 'g' & 'm' & 'i' },
          replaceBy: string
        }
      }) & { applyTo?: SDTFQuery }
    }
    {
      "Text styles": {
        "Body": {
          "$type": "textStyle",
          "$value": {
            "default": {
              "font": {
                "family": "Neue Haas Grotesk Text Pro",
                "postScriptName": "Neue Haas Grotesk Text Pro",
                "weight": "roman",
                "style": "normal",
                "files": []
              },
              "fontSize": {
                "value": 14,
                "unit": "px"
              },
              "color": null,
              "fontFeatures": null,
              "lineHeight": {
                "value": 21,
                "unit": "px"
              },
              "letterSpacing": {
                "value": 0.2,
                "unit": "px"
              },
              "paragraphSpacing": {
                "value": 0,
                "unit": "px"
              },
              "textAlignHorizontal": null,
              "textAlignVertical": null,
              "textDecoration": "none",
              "textIndent": {
                "value": 0,
                "unit": "px"
              },
              "textTransform": "none"
            }
          },
          "$extensions": {
            "com.specifyapp.figmaStyles.hangingList": false,
            "com.specifyapp.figmaStyles.leadingTrim": "NONE",
            "com.specifyapp.figmaStyles.listSpacing": 0,
            "com.specifyapp.figmaStyles.hangingPunctuation": false
          }
        },
        "Body-lg": {
          "$type": "textStyle",
          "$value": {
            "default": {
              "font": {
                "family": "Neue Haas Grotesk Text Pro",
                "postScriptName": "Neue Haas Grotesk Text Pro",
                "weight": "roman",
                "style": "normal",
                "files": []
              },
              "fontSize": {
                "value": 16,
                "unit": "px"
              },
              "color": null,
              "fontFeatures": null,
              "lineHeight": {
                "value": 150,
                "unit": "%"
              },
              "letterSpacing": {
                "value": 0.4,
                "unit": "px"
              },
              "paragraphSpacing": {
                "value": 0,
                "unit": "px"
              },
              "textAlignHorizontal": null,
              "textAlignVertical": null,
              "textDecoration": "none",
              "textIndent": {
                "value": 0,
                "unit": "px"
              },
              "textTransform": "none"
            }
          },
          "$extensions": {
            "com.specifyapp.figmaStyles.hangingList": false,
            "com.specifyapp.figmaStyles.leadingTrim": "NONE",
            "com.specifyapp.figmaStyles.listSpacing": 0,
            "com.specifyapp.figmaStyles.hangingPunctuation": false
          }
        },
        "Body-sm": {
          "$type": "textStyle",
          "$value": {
            "default": {
              "font": {
                "family": "Neue Haas Grotesk Text Pro",
                "postScriptName": "Neue Haas Grotesk Text Pro",
                "weight": "roman",
                "style": "normal",
                "files": []
              },
              "fontSize": {
                "value": 12,
                "unit": "px"
              },
              "color": null,
              "fontFeatures": null,
              "lineHeight": {
                "value": 150,
                "unit": "%"
              },
              "letterSpacing": {
                "value": 0.2,
                "unit": "px"
              },
              "paragraphSpacing": {
                "value": 0,
                "unit": "px"
              },
              "textAlignHorizontal": null,
              "textAlignVertical": null,
              "textDecoration": "none",
              "textIndent": {
                "value": 0,
                "unit": "px"
              },
              "textTransform": "none"
            }
          },
          "$extensions": {
            "com.specifyapp.figmaStyles.hangingList": false,
            "com.specifyapp.figmaStyles.leadingTrim": "NONE",
            "com.specifyapp.figmaStyles.listSpacing": 0,
            "com.specifyapp.figmaStyles.hangingPunctuation": false
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Generate text styles",
          "parsers": [
            {
              "name": "to-css-text-style",
              "output": {
                "type": "file",
                "filePath": "public/text-styles.scss"
              },
              "options": {
                "genericFamily": "sans-serif"
              }
            }
          ]
        }
      ]
    }
    public/text-styles.css
    .Text-styles-Body {
    	font-family: Neue Haas Grotesk Text Pro, sans-serif;
    	font-size: 14px;
    	line-height: 21px;
    	letter-spacing: 0.2px;
    	font-weight: roman;
    }
    .Text-styles-Body-lg {
    	font-family: Neue Haas Grotesk Text Pro, sans-serif;
    	font-size: 16px;
    	line-height: 150%;
    	letter-spacing: 0.4px;
    	font-weight: roman;
    }
    .Text-styles-Body-sm {
    	font-family: Neue Haas Grotesk Text Pro, sans-serif;
    	font-size: 12px;
    	line-height: 150%;
    	letter-spacing: 0.2px;
    	font-weight: roman;
    }
    interface parser {
      name: 'to-css-text-style';
      output: {
        type: 'file';
        filePath: string;
      };
      options?: {
        tokenNameTemplate?: string;
        exclude?: Array<
          | 'fontFamily'
          | 'fontWeight'
          | 'fontSize'
          | 'color'
          | 'fontFeatures'
          | 'lineHeight'
          | 'letterSpacing'
          | 'paragraphSpacing'
          | 'textAlignHorizontal'
          | 'textAlignVertical'
          | 'textDecoration'
          | 'textIndent'
          | 'textTransform'
        >;
        include?: Array<
          | 'fontFamily'
          | 'fontWeight'
          | 'fontSize'
          | 'color'
          | 'fontFeatures'
          | 'lineHeight'
          | 'letterSpacing'
          | 'paragraphSpacing'
          | 'textAlignHorizontal'
          | 'textAlignVertical'
          | 'textDecoration'
          | 'textIndent'
          | 'textTransform'
        >;
        genericFamily?: string;
      };
    }
    {
      "Text styles": {
        "Body": {
          "$type": "textStyle",
          "$value": {
            "default": {
              "font": {
                "family": "Neue Haas Grotesk Text Pro",
                "postScriptName": "Neue Haas Grotesk Text Pro",
                "weight": "roman",
                "style": "normal",
                "files": []
              },
              "fontSize": {
                "value": 14,
                "unit": "px"
              },
              "color": null,
              "fontFeatures": null,
              "lineHeight": {
                "value": 21,
                "unit": "px"
              },
              "letterSpacing": {
                "value": 0.2,
                "unit": "px"
              },
              "paragraphSpacing": {
                "value": 0,
                "unit": "px"
              },
              "textAlignHorizontal": null,
              "textAlignVertical": null,
              "textDecoration": "none",
              "textIndent": {
                "value": 0,
                "unit": "px"
              },
              "textTransform": "none"
            }
          },
          "$extensions": {
            "com.specifyapp.figmaStyles.hangingList": false,
            "com.specifyapp.figmaStyles.leadingTrim": "NONE",
            "com.specifyapp.figmaStyles.listSpacing": 0,
            "com.specifyapp.figmaStyles.hangingPunctuation": false
          }
        },
        "Body-lg": {
          "$type": "textStyle",
          "$value": {
            "default": {
              "font": {
                "family": "Neue Haas Grotesk Text Pro",
                "postScriptName": "Neue Haas Grotesk Text Pro",
                "weight": "roman",
                "style": "normal",
                "files": []
              },
              "fontSize": {
                "value": 16,
                "unit": "px"
              },
              "color": null,
              "fontFeatures": null,
              "lineHeight": {
                "value": 150,
                "unit": "%"
              },
              "letterSpacing": {
                "value": 0.4,
                "unit": "px"
              },
              "paragraphSpacing": {
                "value": 0,
                "unit": "px"
              },
              "textAlignHorizontal": null,
              "textAlignVertical": null,
              "textDecoration": "none",
              "textIndent": {
                "value": 0,
                "unit": "px"
              },
              "textTransform": "none"
            }
          },
          "$extensions": {
            "com.specifyapp.figmaStyles.hangingList": false,
            "com.specifyapp.figmaStyles.leadingTrim": "NONE",
            "com.specifyapp.figmaStyles.listSpacing": 0,
            "com.specifyapp.figmaStyles.hangingPunctuation": false
          }
        },
        "Body-sm": {
          "$type": "textStyle",
          "$value": {
            "default": {
              "font": {
                "family": "Neue Haas Grotesk Text Pro",
                "postScriptName": "Neue Haas Grotesk Text Pro",
                "weight": "roman",
                "style": "normal",
                "files": []
              },
              "fontSize": {
                "value": 12,
                "unit": "px"
              },
              "color": null,
              "fontFeatures": null,
              "lineHeight": {
                "value": 150,
                "unit": "%"
              },
              "letterSpacing": {
                "value": 0.2,
                "unit": "px"
              },
              "paragraphSpacing": {
                "value": 0,
                "unit": "px"
              },
              "textAlignHorizontal": null,
              "textAlignVertical": null,
              "textDecoration": "none",
              "textIndent": {
                "value": 0,
                "unit": "px"
              },
              "textTransform": "none"
            }
          },
          "$extensions": {
            "com.specifyapp.figmaStyles.hangingList": false,
            "com.specifyapp.figmaStyles.leadingTrim": "NONE",
            "com.specifyapp.figmaStyles.listSpacing": 0,
            "com.specifyapp.figmaStyles.hangingPunctuation": false
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "to-scss-mixin-text-style",
          "output": {
            "type": "file",
            "filePath": "public/text-styles.scss"
          },
          "options": {
            "genericFamily": "sans-serif"
          }
        }
      ]
    }
    public/text-styles.scss
    @mixin Text-styles-Body {
    	font-family: Neue Haas Grotesk Text Pro, sans-serif;
    	font-size: 14px;
    	line-height: 1.5;
    	letter-spacing: 0.2px;
    	font-weight: roman;
    }
    @mixin Text-styles-Body-lg {
    	font-family: Neue Haas Grotesk Text Pro, sans-serif;
    	font-size: 16px;
    	line-height: 1.5;
    	letter-spacing: 0.4px;
    	font-weight: roman;
    }
    @mixin Text-styles-Body-sm {
    	font-family: Neue Haas Grotesk Text Pro, sans-serif;
    	font-size: 12px;
    	line-height: 1.5;
    	letter-spacing: 0.2px;
    	font-weight: roman;
    }
    interface parser {
      name: 'to-scss-mixin-text-style';
      output: {
        type: 'file';
        filePath: string;
      };
      options?: {
        tokenNameTemplate?: string;
        exclude?: Array<
          | 'fontFamily'
          | 'fontWeight'
          | 'fontSize'
          | 'color'
          | 'fontFeatures'
          | 'lineHeight'
          | 'letterSpacing'
          | 'paragraphSpacing'
          | 'textAlignHorizontal'
          | 'textAlignVertical'
          | 'textDecoration'
          | 'textIndent'
          | 'textTransform'
        >;
        include?: Array<
          | 'fontFamily'
          | 'fontWeight'
          | 'fontSize'
          | 'color'
          | 'fontFeatures'
          | 'lineHeight'
          | 'letterSpacing'
          | 'paragraphSpacing'
          | 'textAlignHorizontal'
          | 'textAlignVertical'
          | 'textDecoration'
          | 'textIndent'
          | 'textTransform'
        >;
        genericFamily?: string;
      };
    }
    tokens.json
    {
      "Fonts": {
        "Inter Regular": {
          "$type": "font",
          "$value": {
            "default": {
              "family": "Inter Regular",
              "postScriptName": "Inter Regular",
              "weight": "regular",
              "style": "normal",
              "files": [
                {
                  "format": "ttf",
                  "url": "https://static.specifyapp.com/sdtf-seeds/inter-regular.ttf",
                  "provider": "external"
                }
              ]
            }
          }
        }
      }
    }
    type Font = {
      $type: 'font';
      $value: {
        [mode: string]: {
          family: string;
          postScriptName: string;
          weight:
            | 'thin'
            | 'hairline'
            | 'extra-light'
            | 'ultra-light'
            | 'light'
            | 'normal'
            | 'plain'
            | 'standard'
            | 'regular'
            | 'roman'
            | 'book'
            | 'medium'
            | 'semi-bold'
            | 'demi-bold'
            | 'bold'
            | 'extra-bold'
            | 'ultra-bold'
            | 'black'
            | 'heavy'
            | 'extra-black'
            | 'ultra-black'
            | number; // [100-1000]
          style: 'normal' | 'italic';
          files: Array<{
            url: string;
            format: 'ttf' | 'woff' | 'woff2' | 'otf' | 'eot';
            provider: 'external' | 'Specify' | 'Google Fonts' | 'Adobe Fonts';
          }>;
        };
      };
    };
    Font Files parser rule
    {
      "name": "Font files",
      "parsers": [
        {
          "name": "filter",
          "options": {
            "query": {
              "where": { "group": "^Fonts$", "select": { "children": true } }
            }
          }
        },
        {
          "name": "change-case",
          "options": {
            "change": "names",
            "toCase": "kebabCase"
          }
        },
        {
          "name": "to-file",
          "options": {
            "filenameTemplate": "fonts/{{name}}{{extension}}"
          },
          "output": {
            "type": "directory",
            "directoryPath": "./public"
          }
        }
      ]
    }
    interface parser {
      name: 'to-react-native';
      output: {
        type: 'file';
        filePath: string;
      };
      options?: {
        typescript?: boolean;
        moduleExport?: 'es6' | 'commonjs';
      };
    }
    interface parser {
      name: 'to-css-custom-properties';
      output: {
        type: 'file';
        filePath: string;
      };
      options?: {
        tokenNameTemplate?: string;
        selectorTemplate?: string;
        tokenNotInCollectionNameTemplate?: string;
        includeCoreTokensInScopes?: boolean;
        allowUnresolvable?: boolean;
        withSDTFView?: string;
      };
    }

    exclude

    false

    Array<string>

    List of properties to exclude in the CSS class

    include

    false

    Array<string>

    List of properties to include in the CSS class

    genericFamily

    false

    string

    The generic font family will be applied after the main font family

    exclude

    false

    Array<string>

    List of properties to exclude in the SCSS mixins

    include

    false

    Array<string>

    List of properties to include in the SCSS mixins

    genericFamily

    false

    string

    The generic font family will be applied after the main font family

    allowUnresolvableAliases

    false

    boolean

    true

    Whether to allow unresolvable aliases to flow through. This option is only available when resolveAliases = true

    deduplicate

    false

    true | undefined

    undefined

    When you target tokens from different areas in your graph you can end up with tokens that will have the same name which will lead to an override. When set to true, this option will suffix tokens with a -{number} to prevent the override.

    failOnMutate

    false

    true | undefined

    undefined

    By default, this parser mutates your graph. When set to true this option will make your pipeline return an error when your tokens respective path differ from their original one in the graph. Set this option to true if you want to be 100% aligned between design and code.

    SDTFQuery
    "$collection": {
    "$modes": [
    "light",
    "dark"
    ]
    },
    "Core": {
    "blue-100": {
    "$type": "color",
    "$value": {
    "dark": {
    "red": 229,
    "blue": 29,
    "alpha": 1,
    "green": 29,
    "model": "rgb"
    },
    "light": {
    "red": 255,
    "blue": 255,
    "alpha": 1,
    "green": 255,
    "model": "rgb"
    }
    },
    "$description": "token 1 aliased with n modes within collection within n groups"
    },
    "blue-700": {
    "$type": "color",
    "$value": {
    "dark": {
    "red": 229,
    "blue": 0,
    "alpha": 1,
    "green": 0,
    "model": "rgb"
    },
    "light": {
    "red": 255,
    "blue": 255,
    "alpha": 1,
    "green": 200,
    "model": "rgb"
    }
    },
    "$description": "token 2 aliased with n modes within collection within n groups"
    }
    },
    "semantic": {
    "background": {
    "button": {
    "primary": {
    "hover": {
    "$type": "color",
    "$value": {
    "dark": {
    "$mode": "dark",
    "$alias": "colors.Core.blue-100"
    },
    "light": {
    "$mode": "light",
    "$alias": "colors.Core.blue-700"
    }
    },
    "$description": "alias token with n modes within collection within n groups"
    }
    }
    }
    }
    }
    }
    }

    group

    false

    Select all groups in your SDTF token graph.

    collection

    false

    Select all collections in your SDTF token graph.

    token

    false

    Select all tokens in your SDTF token graph.

    regex

    required

    object | string

    If string: the parameter used for the constructor of the regexarrow-up-right. If your use case need to use flags prefer object notation.

    regex.pattern

    required

    string

    The pattern of the regex used as first argument of the constructorarrow-up-right.

    regex.flags

    false

    string

    The flags to use for regex. In the regex constructor it's the second argument constructor of the regexarrow-up-right.

    replaceBy

    required

    string

    The value will used as replacement. This methodarrow-up-right is used to apply the replacement.

    trim

    false

    boolean

    false

    Set true to remove spaces before and after the transformed values. This methodarrow-up-right is used to trim.

    applyTo

    false

    The selection where to apply the transformation. collection, group, token take a Regex string or true to select anything of the kind. An SDTFQuery can be used for advance use cases.

    hashtag
    aliases

    This getter returns the stateful alias references of the token.

    hashtag
    type

    This getter returns the type of the token.

    hashtag
    value

    This getter returns the SDTF JSON representation of the token value.

    hashtag
    modes

    This getter returns the resolved modes of the token.

    hashtag
    isTopLevelAlias

    This getter indicates whether the token is a top level alias.

    hashtag
    isFullyResolvable

    This getter indicates whether the token holds unresolvable aliases.

    hashtag
    modesResolvability

    This getter returns a map representation of whether the token holds unresolvable aliases per mode.

    hashtag
    definition

    This getter returns the design token definition - containing validation schemas of the token.

    hashtag
    Methods

    hashtag
    getCollection

    This method returns the parent collection of the token if any.

    hashtag
    getStatefulValueResult

    This method returns a StatefulValueResult instance allowing to map over the token possible values for modes and aliases.

    hashtag
    resolveDeepStatefulValueForMode

    This method resolves the stateful value for a given mode.

    hashtag
    getUIValueResultOnMode

    This method returns the Stateful Value representation for frontend usage.

    hashtag
    rename

    This method renames the token.

    hashtag
    renameMode

    This method renames a mode of the token.

    hashtag
    updateValue

    This method updates the whole value of the token (including modes).

    hashtag
    updateModeValue

    This method updates the value of a specific mode of the token.

    hashtag
    resolveValueAliases

    This method resolves the aliases from the token.

    hashtag
    createModeValue

    This method creates a new mode for the token.

    hashtag
    deleteModeValue

    This method deletes a mode of the token.

    hashtag
    getJSONValue

    This method returns the JSON representation of the Token value.

    hashtag
    getJSONToken

    This method returns the JSON representation of the Token.

    hashtag
    move

    This method moves the item to the specified path.

    hashtag
    matchByType

    This method matches the token by type.

    hashtag
    matchJSONValueByType

    This method matches the JSON value of the token by type.

    hashtag
    toTokenStateParams

    This method returns the token state parameters.

    hashtag
    toAnalyzedToken

    This method returns the analyzed token.

    hashtag
    Matchers

    hashtag
    isString

    This method checks if the token state is of type 'string'.

    hashtag
    isNumber

    This method checks if the token state is of type 'number'.

    hashtag
    isBoolean

    This method checks if the token state is of type 'boolean'.

    hashtag
    isNull

    This method checks if the token state is of type 'null'.

    hashtag
    isArray

    This method checks if the token state is of type 'array'.

    hashtag
    isObject

    This method checks if the token state is of type 'object'.

    hashtag
    isIntegerNumber

    This method checks if the token state is of type 'integerNumber'.

    hashtag
    isZeroToOneNumber

    This method checks if the token state is of type 'zeroToOneNumber'.

    hashtag
    isArcDegreeNumber

    This method checks if the token state is of type 'arcDegreeNumber'.

    hashtag
    isRgbColorNumber

    This method checks if the token state is of type 'rgbColorNumber'.

    hashtag
    isPositiveNumber

    This method checks if the token state is of type 'positiveNumber'.

    hashtag
    isPositiveIntegerNumber

    This method checks if the token state is of type 'positiveIntegerNumber'.

    hashtag
    isPercentageNumber

    This method checks if the token state is of type 'percentageNumber'.

    hashtag
    isHexadecimalColorString

    This method checks if the token state is of type 'hexadecimalColorString'.

    hashtag
    isBitmap

    This method checks if the token state is of type 'bitmap'.

    hashtag
    isBitmapFormat

    This method checks if the token state is of type 'bitmapFormat'.

    hashtag
    isBlur

    This method checks if the token state is of type 'blur'.

    hashtag
    isBorder

    This method checks if the token state is of type 'border'.

    hashtag
    isBorderStyle

    This method checks if the token state is of type 'borderStyle'.

    hashtag
    isBorderStyleLineCap

    This method checks if the token state is of type 'borderStyleLineCap'.

    hashtag
    isBreakpoint

    This method checks if the token state is of type 'breakpoint'.

    hashtag
    isColor

    This method checks if the token state is of type 'color'.

    hashtag
    isCubicBezier

    This method checks if the token state is of type 'cubicBezier'.

    hashtag
    isDimension

    This method checks if the token state is of type 'dimension'.

    hashtag
    isDimensionUnit

    This method checks if the token state is of type 'dimensionUnit'.

    hashtag
    isDuration

    This method checks if the token state is of type 'duration'.

    hashtag
    isDurationUnit

    This method checks if the token state is of type 'durationUnit'.

    hashtag
    isFont

    This method checks if the token state is of type 'font'.

    hashtag
    isFontFamily

    This method checks if the token state is of type 'fontFamily'.

    hashtag
    isFontFeature

    This method checks if the token state is of type 'fontFeature'.

    hashtag
    isFontFeatures

    This method checks if the token state is of type 'fontFeatures'.

    hashtag
    isFontFormat

    This method checks if the token state is of type 'fontFormat'.

    hashtag
    isFontStyle

    This method checks if the token state is of type 'fontStyle'.

    hashtag
    isFontWeight

    This method checks if the token state is of type 'fontWeight'.

    hashtag
    isGradient

    This method checks if the token state is of type 'gradient'.

    hashtag
    isGradients

    This method checks if the token state is of type 'gradients'.

    hashtag
    isOpacity

    This method checks if the token state is of type 'opacity'.

    hashtag
    isRadius

    This method checks if the token state is of type 'radius'.

    hashtag
    isRadii

    This method checks if the token state is of type 'radii'.

    hashtag
    isShadow

    This method checks if the token state is of type 'shadow'.

    hashtag
    isShadows

    This method checks if the token state is of type 'shadows'.

    hashtag
    isShadowType

    This method checks if the token state is of type 'shadowType'.

    hashtag
    isSpacing

    This method checks if the token state is of type 'spacing'.

    hashtag
    isSpacings

    This method checks if the token state is of type 'spacings'.

    hashtag
    isStepsTimingFunction

    This method checks if the token state is of type 'stepsTimingFunction'.

    hashtag
    isTextAlignHorizontal

    This method checks if the token state is of type 'textAlignHorizontal'.

    hashtag
    isTextAlignVertical

    This method checks if the token state is of type 'textAlignVertical'.

    hashtag
    isTextDecoration

    This method checks if the token state is of type 'textDecoration'.

    hashtag
    isTextStyle

    This method checks if the token state is of type 'textStyle'.

    hashtag
    isTextTransform

    This method checks if the token state is of type 'textTransform'.

    hashtag
    isTransition

    This method checks if the token state is of type 'transition'.

    hashtag
    isVector

    This method checks if the token state is of type 'vector'.

    hashtag
    isVectorFormat

    This method checks if the token state is of type 'vectorFormat'.

    hashtag
    isZIndex

    This method checks if the token state is of type 'zIndex'.

    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Only get tokens from a group named 'info' and gererate tokens in JSON",
          "parsers": [
            {
              "name": "filter",
              "options": {
                "query": {
                  "where": {
                    "group": "info",
                    "select": {
                      "parents": true,
                      "children": true
                    }
                  }
                }
              }
            },
            {
              "name": "to-sdtf",
              "output": {
                "type": "file",
                "filePath": "tokens.json"
              }
            }
          ]
        }
      ]
    }
    tokens.json
    {
      "colors": {
        "$collection": {
          "$modes": ["light", "dark"]
        },
        "info": {
          "infoToken": {
            "$type": "color",
            "$value": {
              "light": {
                "model": "rgb",
                "red": 219,
                "green": 234,
                "blue": 254,
                "alpha": 1
              },
              "dark": {
                "model": "rgb",
                "red": 219,
                "green": 234,
                "blue": 254,
                "alpha": 1
              }
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "To React Native theme",
          "parsers": [
            {
              "name": "to-react-native",
              "output": {
                "type": "file",
                "filePath": "public/theme.js"
              }
            }
          ]
        }
      ]
    }
    tokens.js
    /** 
    * @typedef {'primitive.spacing.1'} DimensionPath - All the valid paths for the tokens of type dimension.
    * To use this type you can do: `@type {import('path/to/myTokensFile').DimensionPath}`
    */
    /** 
    * @typedef {'themedColor.highEmphasis'} ColorPath - All the valid paths for the tokens of type color.
    * To use this type you can do: `@type {import('path/to/myTokensFile').ColorPath}`
    */
    /**
    * @typedef {DimensionPath | ColorPath} AllPath - All possible paths
    */
    /**
    * @typedef {typeof pathsByType} PathsByType - All the paths for a given token type. Needed for `getTokensByType`
    */
    const pathsByType = /** @type {const} */ ({
      dimension: [ 'primitive.spacing.1' ],
      color: [ 'themedColor.highEmphasis' ]
    });
    
    /**
    * @typedef {typeof colorModes[number]} ColorModes - All the valid modes of color.
    * To use this type you can do: `@type {import('path/to/myTokensFile').ColorModes}`
    */
    export const colorModes = /** @type {const} */ ([ 'dark', 'light' ]);
    
    /**
    * @typedef {typeof themedColorModes[number]} ThemedcolorModes - All the valid modes of themedColor.
    * To use this type you can do: `@type {import('path/to/myTokensFile').ThemedcolorModes}`
    */
    export const themedColorModes = /** @type {const} */ ([ 'light', 'dark' ]);
    
    /**
    @typedef {ColorModes | ThemedcolorModes} AllMode - All the available modes
    */
    
    /** 
    * @typedef {typeof tokens} Tokens - All the tokens. 
    * Use `getTokenByMode` to retrieve one. 
    */
    export const tokens = /** @type {const} */ ({
      'primitive.spacing.1': '4px',
      'themedColor.highEmphasis': { dark: '#ffffff', light: '#000000' }
    });
    
    /**
    * Retrieve any token for a given mode. If available, the default mode will be: 'default'
    * @template {AllPath} Path - A generic extending all the possible paths
    * @template {Tokens[Path] extends Record<string, any>
        ? keyof Tokens[Path]
        : undefined} Mode - A generic representing all the valid modes for a given path
    * @template {Tokens[Path] extends Record<string, any>
        ? Tokens[Path][Mode extends undefined ? never : Mode]
        : Tokens[Path]} Return - The return type
    * @param {Path} path - The path to the token
    * @param {Mode} mode - The mode of the token you want to retrieve 
    * @returns {Return} - The value of a token for a given mode
    */
    export function getTokenByMode(path, mode) {
      if (!tokens[path]) {
        throw new Error("Path: '" + path + "' doesn't exist. Here are all the valid paths:\n- " + Object.keys(tokens).join('\n- '))
      }
    
      if (typeof tokens[path] !== 'object') {
        return tokens[path] ;
      }
    
      if (!mode) throw new Error('Mode is undefined but it should be one of ' + Object.keys(tokens[path]).join(', ') + ' for path: ' + path);
    
      if (!tokens[path][mode]) {
        throw new Error("Invalid mode '" + mode.toString() + " at path '" + path + "', here are all the valid modes:\n- " + Object.keys(tokens[path]).join('\n- '))
      } 
    
      return tokens[path][mode]  
    }
    
    /**
    * Retrieve all the tokens for a specific type (color, dimension, etc...).
    * Note that the value will either be a string or an object if the token has modes
    * @template {keyof PathsByType} Type - A generic extending all the possible types
    * @template {Tokens[PathsByType[Type][number]]} Token - A generic representing a union of all the outputs
    * @param {Type} type - The path to the token
    * @returns {Array<Token>} - An array with all the values
    */
    export function getTokensByType(type) {
      if (!pathsByType[type]) {
        throw new Error('The type: \'' + type + '\' does not exist')
      }
    
      return pathsByType[type].map(path => tokens[path]);
    }
    string | {
      regex: string | { pattern: string, flags: 'g' & 'm' & 'i' },
      replaceBy: string
    }
    string | {
      regex: string | { pattern: string, flags: 'g' & 'm' & 'i' },
      replaceBy: string
    }
    string | {
      regex: string | { pattern: string, flags: 'g' & 'm' & 'i' },
      replaceBy: string
    }
    | { collection: string | true }
    | { group: string | true }
    | { token: string | true }
    | SDTFQuery
    string | {
      regex: string | { pattern: string, flags: 'g' & 'm' & 'i' },
      replaceBy: string
    }
    get aliases: Array<StatefulAliasReference>;
    get type: SpecifyDesignTokenTypeName;
    get value: SpecifyDesignToken['$value'];
    get modes: Array<string>;
    get isTopLevelAlias: boolean;
    get isFullyResolvable: boolean;
    get modesResolvability: Record<string, boolean>;
    get definition: DesignTokenDefinition<Type>;
    getCollection(): import("./CollectionState.js").CollectionState | undefined;
    getStatefulValueResult(): StatefulValueResult<Type>;
    resolveDeepStatefulValueForMode<T extends SpecifyDesignTokenTypeName = Type>(mode: string): ResolvedDeepStatefulValueForMode<T>;
    getUIValueResultOnMode(targetMode: string): UIValueResultSignature<Type>;
    rename(newName: string): boolean;
    renameMode(fromMode: string, toMode: string): void;
    updateValue(nextValue: Partial<PickSpecifyDesignToken<Type, string, false, true>['$value']>, { allowModeCreation, overrideAliases }?: UpdateOptions): void;
    updateModeValue(mode: string, nextValue: Partial<PickSpecifyDesignToken<Type, string, true, false>['$value']>, { overrideAliases, allowModeCreation }?: UpdateOptions): void;
    resolveValueAliases(): void;
    createModeValue(mode: string, nextValue: PickSpecifyDesignToken<Type, string, true, false>['$value']): void;
    deleteModeValue(mode: string): void;
    getJSONValue<ResolveAliases extends boolean, AllowUnresolvable extends boolean = true, TargetMode extends Mode | null = null>(options?: GetJSONTokenValueOptions<ResolveAliases, AllowUnresolvable, TargetMode>): PickSpecifyDesignToken<Type, Mode, AllowUnresolvable extends true ? true : false, TargetMode extends string ? false : true>['$value'];
    getJSONToken<ResolveAliases extends boolean, AllowUnresolvable extends boolean = true, TargetMode extends Mode | null = null>(options?: GetJSONTokenValueOptions<ResolveAliases, AllowUnresolvable, TargetMode>): PickSpecifyDesignToken<Type, Mode, AllowUnresolvable extends true ? true : false, TargetMode extends string ? false : true>;
    move(toPath: Array<string>): void;
    matchByType<Return>(matcher: {
        [K in SpecifyDesignTokenTypeName]?: (token: TokenState<K, Value, Mode>) => Return;
    }, missingFn: (token: TokenState) => Return): Return;
    matchJSONValueByType<MatchReturn, MissingReturn = undefined>(matcher: {
        [K in Type]?: (token: PickSpecifyDesignToken<K, string, false, false>['$value'], mode: string) => MatchReturn;
    }, missingFn: (token: TokenState) => MissingReturn): {
        [mode: string]: MatchReturn;
    } | MissingReturn | undefined;
    toTokenStateParams(): TokenStateParams;
    toAnalyzedToken(): AnalyzedToken;
    isString(): this is TokenState<'string'>;
    isNumber(): this is TokenState<'number'>;
    isBoolean(): this is TokenState<'boolean'>;
    isNull(): this is TokenState<'null'>;
    isArray(): this is TokenState<'array'>;
    isObject(): this is TokenState<'object'>;
    isIntegerNumber(): this is TokenState<'integerNumber'>;
    isZeroToOneNumber(): this is TokenState<'zeroToOneNumber'>;
    isArcDegreeNumber(): this is TokenState<'arcDegreeNumber'>;
    isRgbColorNumber(): this is TokenState<'rgbColorNumber'>;
    isPositiveNumber(): this is TokenState<'positiveNumber'>;
    isPositiveIntegerNumber(): this is TokenState<'positiveIntegerNumber'>;
    isPercentageNumber(): this is TokenState<'percentageNumber'>;
    isHexadecimalColorString(): this is TokenState<'hexadecimalColorString'>;
    isBitmap(): this is TokenState<'bitmap'>;
    isBitmapFormat(): this is TokenState<'bitmapFormat'>;
    isBlur(): this is TokenState<'blur'>;
    isBorder(): this is TokenState<'border'>;
    isBorderStyle(): this is TokenState<'borderStyle'>;
    isBorderStyleLineCap(): this is TokenState<'borderStyleLineCap'>;
    isBreakpoint(): this is TokenState<'breakpoint'>;
    isColor(): this is TokenState<'color'>;
    isCubicBezier(): this is TokenState<'cubicBezier'>;
    isDimension(): this is TokenState<'dimension'>;
    isDimensionUnit(): this is TokenState<'dimensionUnit'>;
    isDuration(): this is TokenState<'duration'>;
    isDurationUnit(): this is TokenState<'durationUnit'>;
    isFont(): this is TokenState<'font'>;
    isFontFamily(): this is TokenState<'fontFamily'>;
    isFontFeature(): this is TokenState<'fontFeature'>;
    isFontFeatures(): this is TokenState<'fontFeatures'>;
    isFontFormat(): this is TokenState<'fontFormat'>;
    isFontStyle(): this is TokenState<'fontStyle'>;
    isFontWeight(): this is TokenState<'fontWeight'>;
    isGradient(): this is TokenState<'gradient'>;
    isGradients(): this is TokenState<'gradients'>;
    isOpacity(): this is TokenState<'opacity'>;
    isRadius(): this is TokenState<'radius'>;
    isRadii(): this is TokenState<'radii'>;
    isShadow(): this is TokenState<'shadow'>;
    isShadows(): this is TokenState<'shadows'>;
    isShadowType(): this is TokenState<'shadowType'>;
    isSpacing(): this is TokenState<'spacing'>;
    isSpacings(): this is TokenState<'spacings'>;
    isStepsTimingFunction(): this is TokenState<'stepsTimingFunction'>;
    isTextAlignHorizontal(): this is TokenState<'textAlignHorizontal'>;
    isTextAlignVertical(): this is TokenState<'textAlignVertical'>;
    isTextDecoration(): this is TokenState<'textDecoration'>;
    isTextStyle(): this is TokenState<'textStyle'>;
    isTextTransform(): this is TokenState<'textTransform'>;
    isTransition(): this is TokenState<'transition'>;
    isVector(): this is TokenState<'vector'>;
    isVectorFormat(): this is TokenState<'vectorFormat'>;
    isZIndex(): this is TokenState<'zIndex'>;

    false

    string

    The pattern used to generate token names. It must match template syntax. You can use collection, mode,groups and token names.

    tokenNotInCollectionNameTemplate

    false

    string

    The pattern used to generate token names when they are located outside of collections. It must match template syntax. You can use mode,groups and token names.

    includeCoreTokensInScopes

    false

    boolean

    false

    When set to true, you will have both core tokens and alias tokens in each CSS scopes thus making alias tokens always resolvable.

    allowUnresolvable

    false

    boolean

    false

    When set to true, you'll be able to generate alias CSS variables targeting tokens which are not generating in the same scope or file (e.g., if you want a primitive.css and semantic.css files). When set to true, tokens will be named with the tokenNotInCollectionNameTemplate option.

    withSDTFView

    false

    string

    The name of a registered view. See for more details using the CLI or GitHub.

    selectorTemplate

    false

    string

    The pattern used to generate the CSS selector name(s). It must match mustachearrow-up-right template syntax. You can use collection, mode and groups names.

    templates section

    tokenNameTemplate

    Parameter
    Required
    Type
    Default
    Description

    toFormat

    required

    The target color format to convert to. Actual value conversion is done by the package.

    hashtag
    Basic usage

    We convert all colors from our SDTF graph in hsl.

    We then generate our transformed SDTF graph in a JSON file thanks to the to-sdtf parser.

    Utility parsers do not have the output option since they mutate the internal state without producing any actual output.
    Parser
    Description
    Usage example

    This parser helps you change the case of names or modes over a SDTF graph.

    This parser helps you convert the color formats of color compatible tokens over a SDTF graph.

    hashtag
    Generation parsers

    Generation parsers help you generate your token tree into any technology / purposed oriented outputs.

    circle-info

    Many generation parsers can be placed in the same parser rule, as long as they all need the same transformed - or not - token tree and they all use the file or directory output setting.

    Parser
    Description
    Usage example

    This parser helps you transform design tokens in CSS Custom Properties.

    This parser helps you generate text styles as CSS classes.

    Parameter
    Required
    Type
    Default
    Description

    tokenNameTemplate

    false

    string

    {{path}}{{token}}{{mode}}

    The template the parser follows to name your tokens. You can use the path of your tokens, their token name, and their respective mode.

    hashtag
    Basic usage

    Specify Design Token Format

    Learn more about the Specify Design Token Format (SDTF) specifications.

    hashtag
    Introduction

    The Specify Design Token Format (SDTF) is a JSON based notation made to capture the architecture of information of design tokens regardless of its source. Supporting 50+ token types, the SDTF allows to compose the most complex types by referencing/aliasing others. Its primary focus is compatibility among design (system) tools and the frontend technologies used to build design systems.

    In Specify, each repository is home to one SDTF token tree.

    Retrieving and working with the tokens

    hashtag
    Get a SDTFClient

    The is a class providing numerous methods to work with the design data stored using the Specify Design Token Format (SDTF).

    It's the way of interacting with your tokens and generating content.

    change-case

    This parser helps you change the case of names or modes over a SDTF graph.

    hashtag
    Interface

    hashtag
    Options

    Specify CLI VS Specify SDK

    circle-info

    TL;DR - The CLI is faster to get started, but the SDK is more flexible and can match exactly what you need

    Historically, if you wanted to convert your tokens to various outputs: , , ... you could only distribute your tokens thanks to a configuration file. With the introduction of the , you might want to know which option suits your needs, so let's compare them!

    This page will be an overview of both options, so if you need more details, you can have look to the page, and the page.

    {
      "colors": {
        "$collection": { "$modes": ["light", "dark"] },
        "core": {
          "blue-100": {
            "$type": "color",
            "$description": "token 1 aliased with n modes within collection within n groups",
            "$value": {
              "light": {
                "red": 219,
                "blue": 254,
                "alpha": 1,
                "green": 236,
                "model": "rgb"
              },
              "dark": {
                "red": 41,
                "blue": 67,
                "alpha": 1,
                "green": 52,
                "model": "rgb"
              }
            }
          },
          "blue-700": {
            "$type": "color",
            "$description": "token 2 aliased with n modes within collection within n groups",
            "$value": {
              "light": {
                "red": 17,
                "blue": 249,
                "alpha": 1,
                "green": 125,
                "model": "rgb"
              },
              "dark": {
                "red": 96,
                "blue": 250,
                "alpha": 1,
                "green": 168,
                "model": "rgb"
              }
            }
          }
        },
        "semantic": {
          "background": {
            "button": {
              "primary": {
                "hover": {
                  "$type": "color",
                  "$description": "alias token with n modes within collection within n groups",
                  "$value": {
                    "dark": {
                      "$mode": "dark",
                      "$alias": "colors.core.blue-100"
                    },
                    "light": {
                      "$mode": "light",
                      "$alias": "colors.core.blue-700"
                    }
                  }
                }
              }
            }
          }
        }  
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Generate tokens as CSS Custom Properties",
          "parsers": [
            {
              "name": "to-css-custom-properties",
              "output": {
                "type": "file",
                "filePath": "tokens.css"
              },
              "options": {
                "tokenNameTemplate": "--{{groups}}-{{token}}",
                "selectorTemplate": "[data-theme=\"{{mode}}\"]"
              }
            }
          ]
        }
      ]
    }
    tokens.css
    [data-theme="dark"] {
      --core-blue-100: rgb(41, 52, 67);
      --core-blue-700: rgb(96, 168, 250);
      --semantic-background-button-primary-hover: var(--core-blue-100);
    }
    [data-theme="light"] {
      --core-blue-100: rgb(219, 236, 254);
      --core-blue-700: rgb(17, 125, 249);
      --semantic-background-button-primary-hover: var(--core-blue-700);
    }
    {
      "Colors": {
        "$collection": { "$modes": ["Light", "Dark"] },
        "Core": {
          "blue 100": {
            "$type": "color",
            "$description": "token 1 aliased with n modes within collection within n groups",
            "$value": {
              "Light": {
                "red": 219,
                "blue": 254,
                "alpha": 1,
                "green": 236,
                "model": "rgb"
              },
              "Dark": {
                "red": 41,
                "blue": 67,
                "alpha": 1,
                "green": 52,
                "model": "rgb"
              }
            }
          },
          "blue 700": {
            "$type": "color",
            "$description": "token 2 aliased with n modes within collection within n groups",
            "$value": {
              "Light": {
                "red": 17,
                "blue": 249,
                "alpha": 1,
                "green": 125,
                "model": "rgb"
              },
              "Dark": {
                "red": 96,
                "blue": 250,
                "alpha": 1,
                "green": 168,
                "model": "rgb"
              }
            }
          }
        },
        "semantic": {
          "background": {
            "button": {
              "primary": {
                "hover": {
                  "$type": "color",
                  "$description": "alias token with n modes within collection within n groups",
                  "$value": {
                    "Dark": {
                      "$mode": "dark",
                      "$alias": "Colors.Core.blue 100"
                    },
                    "Light": {
                      "$mode": "light",
                      "$alias": "Colors.Core.blue 700"
                    }
                  }
                }
              }
            }
          }
        }  
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Convert colors in HSL and generate tokens in JSON",
          "parsers": [
            {
              "name": "convert-color",
              "options": {
                "toFormat": "hsl"
              }
            },
            {
              "name": "to-sdtf",
              "output": {
                "type": "file",
                "filePath": "tokens.json"
              }
            }
          ]
        }
      ]
    }
    tokens.json
    {
      "colors": {
        "$collection": { "$modes": ["light", "dark"] },
        "core": {
          "blue-100": {
            "$type": "color",
            "$description": "token 1 aliased with n modes within collection within n groups",
            "$value": {
              "light": {
                "model": "hsl",
                "hue": 211,
                "saturation": 95,
                "lightness": 93,
                "alpha": 1
              },
              "dark": {
                "model": "hsl",
                "hue": 215,
                "saturation": 24,
                "lightness": 21,
                "alpha": 1
              }
            }
          },
          "blue-700": {
            "$type": "color",
            "$description": "token 2 aliased with n modes within collection within n groups",
            "$value": {
              "light": {
                "model": "hsl",
                "hue": 212,
                "saturation": 95,
                "lightness": 52,
                "alpha": 1
              },
              "dark": {
                "model": "hsl",
                "hue": 212,
                "saturation": 94,
                "lightness": 68,
                "alpha": 1
              }
            }
          }
        },
        "semantic": {
          "background": {
            "button": {
              "primary": {
                "hover": {
                  "$type": "color",
                  "$description": "alias token with n modes within collection within n groups",
                  "$value": {
                    "dark": {
                      "$mode": "dark",
                      "$alias": "colors.core.blue-100"
                    },
                    "light": {
                      "$mode": "light",
                      "$alias": "colors.core.blue-700"
                    }
                  }
                }
              }
            }
          }
        }  
      }
    }
    interface parser {
      name: 'convert-color';
      options: {
        toFormat:
          | 'hex'
          | 'rgb'
          | 'hsl'
          | 'hsb'
          | 'lch'
          | 'lab';
        applyTo?:
          | { collection: string | true }
          | { group: string | true }
          | { token: string | true }
          | SDTFQuery;
      };
    }
    {
      "colors": {
        "$collection": {
          "$modes": [
            "light",
            "dark"
          ]
        },
        "aliases": {
          "border": {
            "active": {
              "$type": "color",
              "$value": {
                "dark": {
                  "$alias": "colors.core.label.blue-base",
                  "$mode": "dark"
                },
                "light": {
                  "$alias": "colors.core.label.blue-base",
                  "$mode": "light"
                }
              }
            }
          }
        },
        "core": {
          "label": {
            "blue-base": {
              "$type": "color",
              "$value": {
                "dark": {
                  "model": "rgb",
                  "red": 96,
                  "green": 168,
                  "blue": 250,
                  "alpha": 1
                },
                "light": {
                  "model": "rgb",
                  "red": 17,
                  "green": 125,
                  "blue": 249,
                  "alpha": 1
                }
              }
            },
            "blue-lighter": {
              "$type": "color",
              "$value": {
                "dark": {
                  "model": "rgb",
                  "red": 41,
                  "green": 52,
                  "blue": 67,
                  "alpha": 1
                },
                "light": {
                  "model": "rgb",
                  "red": 219,
                  "green": 236,
                  "blue": 254,
                  "alpha": 1
                }
              }
            }
          }
        }
      },
      "dimensions": {
        "$collection": {
          "$modes": [
            "desktop",
            "mobile"
          ]
        },
        "base": {
          "dimension-01": {
            "$type": "dimension",
            "$value": {
              "mobile": {
                "value": 2,
                "unit": "px"
              },
              "desktop": {
                "value": 4,
                "unit": "px"
              }
            }
          },
          "dimension-02": {
            "$type": "dimension",
            "$value": {
              "mobile": {
                "value": 4,
                "unit": "px"
              },
              "desktop": {
                "value": 8,
                "unit": "px"
              }
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "To Kotlin",
          "parsers": [
            {
              "name": "to-kotlin",
              "output": {
                "type": "file",
                "filePath": "public/tokens.kt"
              }
            }
          ]
        }
      ]
    }
    output/tokens.kt
    import android.content.res.Resources
    import androidx.compose.animation.core.CubicBezierEasing
    import androidx.compose.ui.graphics.Color
    import androidx.compose.ui.text.TextStyle
    import androidx.compose.ui.text.font.*
    import androidx.compose.ui.unit.TextUnit
    import androidx.compose.ui.unit.sp
    
    object DesignTokens {
      fun pxToDp(px: Double): Float {
        return (px / Resources.getSystem().displayMetrics.density).toFloat()
      }
      fun pxToDp(px: Int): Float {
        return px / Resources.getSystem().displayMetrics.density
      }
    
      fun pxToSp(px: Double): TextUnit {
        return (px / Resources.getSystem().displayMetrics.scaledDensity).sp
      }
      fun pxToSp(px: Int): TextUnit {
        return (px / Resources.getSystem().displayMetrics.scaledDensity).sp
      }
    
      object Color_ {
        val colorsCoreLabelBlue_baseDark = Color(0.37647f, 0.65882f, 0.98039f, 1f)
        val colorsCoreLabelBlue_baseLight = Color(0.06667f, 0.49020f, 0.97647f, 1f)
        val colorsAliasesBorderActiveDark = Color(0.37647f, 0.65882f, 0.98039f, 1f)
        val colorsAliasesBorderActiveLight = Color(0.06667f, 0.49020f, 0.97647f, 1f)
        val colorsCoreLabelBlue_lighterDark = Color(0.16078f, 0.20392f, 0.26275f, 1f)
        val colorsCoreLabelBlue_lighterLight = Color(0.85882f, 0.92549f, 0.99608f, 1f)
      }
    
      object Dimension {
        val dimensionsBaseDimension_01Desktop = pxToDp(4)
        val dimensionsBaseDimension_01Mobile = pxToDp(2)
        val dimensionsBaseDimension_02Desktop = pxToDp(8)
        val dimensionsBaseDimension_02Mobile = pxToDp(4)
      }
    }
    interface parser {
      name: 'to-kotlin';
      output: {
        type: 'file';
        filePath: string;
      };
      options?:{
        tokenNameTemplate?: string;
        scopeName?: string;
        androidMinVersion?: number;
      }
    }

    scopeName

    false

    string

    DesignTokens

    The name of the parent class which contains all classes for all your token types.

    androidMinVersion

    false

    number

    33

    The min Android version supported by your app.

    mustachearrow-up-right
    mustachearrow-up-right
    register-view

    convert-dimension

    This parser helps you convert units of dimension tokens (spacing, sizing, breakpoint, blur...) and composite tokens sub values (font-size, letter-spacing, border-width...).

    Example

    make-line-height-relative

    This parser helps you transform your text style lineheight values relative to their font size.

    Example

    filter

    This parser helps you filter a SDTF graph.

    Example

    register-view

    This parser helps you register a SDTF view that can be later used by generation parser to allow for partial outputs of the token tree with no destructive mutations.

    Example

    select-modes

    This parser helps you select design tokens from specific mode(s).

    Example

    prefix-by

    This parser helps you prefix the name of your collections, groups and tokens from your SDTF token tree.

    Example

    suffix-by

    This parser helps you suffix the name of your collections, groups and tokens from your SDTF token tree.

    Example

    replace-string

    This parser helps you replace a part or the whole name of your collections, groups and tokens from your SDTF token tree.

    Example

    to-css-font-import

    This parser helps you create CSS @font-face rules to import your font files.

    Example

    to-flutter

    This parser helps you generate a Flutter theme from all your design tokens coming from Specify.

    Example

    to-javascript

    This parser helps you pull design tokens as JavaScript objects for all token types and their respective getter functions.

    Example

    to-json

    This parser helps you pull design tokens in JSON with token values in JSON or CSS.

    Example

    to-json-list

    This parser helps you pull design tokens in JSON within an iterable array structure.

    Example

    to-kotlin

    This parser helps you generate design tokens for Kotlin.

    Example

    to-react-native

    This parser helps you pull design tokens as a theme compatible with React Native and their respective helper functions.

    Example

    to-scss-mixin-text-style

    This parser helps you generate text styles as SCSS mixins.

    Example

    to-scss-map

    This parser helps you generate .scss files for each token type containing SCSS map and function / mixin to access the values of the tokens.

    Example

    to-sdtf

    This parser helps you get your design tokens as a SDTF graph in JSON.

    Example

    to-style-dictionary

    This parser helps you generate Style Dictionaryarrow-up-right raw token files for all your design tokens coming from Specify.

    Example

    to-swift

    This parser helps you generate design tokens as Swift classes.

    Example

    to-tailwind

    This parser helps you generate a Tailwind theme from all your design tokens coming from Specify.

    Example

    to-typescript

    This parser helps you pull design tokens as TypeScript objects for all token types and their respective getter functions.

    Example

    svgo

    This parser help you optimize and transform your SVG strings with svgoarrow-up-right.

    Example

    svg-to-jsx

    This parser helps you generate JSX or TSX components from your vector assets.

    Example

    svg-to-tsx

    This parser helps you generate TSX components from your vector assets.

    Example

    to-svg-file

    This parser helps you generate SVG files from your vector assets.

    Example

    to-bitmap-file

    This parser helps you generate PNG and JPG files from your bitmap assets.

    Example

    to-file

    This parser helps you generate files from any asset token: Bitmap, Font and Vector.

    Example

    change-case
    Example
    convert-color
    Example
    to-css-custom-properties
    Example
    to-css-text-style
    Example

    applyTo

    false

    The selection where to apply the transformation. collection, group, token take a Regex string or true to select anything of the kind. An SDTFQuery can be used for advance use cases.

    colordarrow-up-right
    hashtag
    Get the SDTF JSON token tree

    You would want to grab the SDTF JSON token tree of a repository anytime you want to: send the token tree over the network or debug an intermediate manipulation.

    Now that we know how to retrieve our SDTF, let's see how we can manipulate and retrieve our tokens.

    hashtag
    Retrieving the tokens

    There's multiple way to retrieve a token. If you want to work with a specific one you'll probably want to only get one, if you're developing a generic solution, you'll probably prefer iterating over many tokens. Let's have a look on how to do this.

    hashtag
    Retrieving a single token

    To retrieve a single token, you can just pick it by its path:

    hashtag
    Retrieving all the tokens

    hashtag
    Tokens

    hashtag
    Groups

    hashtag
    Collections

    hashtag
    Retrieving specific tokens

    If you need specifically some tokens, for example, based on the name, type, path, etc... You can perform a query and map over the results:

    Or, you can only iterate over the results of your query if you need to perform mutations for example:

    circle-info

    Looking for details about the query? 👉 heads up to the SDTF Query Language concept.

    circle-info

    Looking for more methods to retrieve tokens? 👉 heads up to the SDTFEngine reference.

    hashtag
    The token architecture

    The TokenState instance will be your main access to your tokens. Through this, you can retrieve your token data or perform updates. But first, let’s put some context and try to figure out what this TokenState is trying to solve.

    If we take a look at the token representation in the SDTF, it’s basically a JSON value where a lot of keys can be an alias rather than a value. Let’s have a look at all the possible cases with an example. Note that the following examples are 4 different ways of achieving the same result from a value point of view.

    hashtag
    A basic token

    hashtag
    An alias at the value level

    hashtag
    An alias at the mode level

    hashtag
    An alias at the top level

    As you can see, there’s a lot of way to express a token, and dealing with all the possible cases is quite difficult. That’s why one of the goal of the TokenState is to help you to deal with everything.

    hashtag
    How to differentiate the tokens?

    Before even thinking about retrieving the value of a token, we need to know what type of token we’re working with.

    It’s actually quite easy to know as there’s a type property in the TokenState :

    But even if we know the type, a problem will remain: Typescript doesn’t. TokenState is quite a generic class as the default type is something that looks like this: TokenState<'dimension' | 'font' | 'breakpoint' | ...> .

    So the first mission is to be able to narrow down the type to be able to work with it. To do so, there’s a pretty convenient function:

    You can match as much type as you need, and if some are not matched, they’ll be caught by the callback in the 2nd argument.

    If you're working with Typescript, you may face some type issues while using matchByType. A common example would be this:

    If you copy/paste this code inside your editor, Typescript will complain and gives you the following error: Type '(_: TokenState<"breakpoint", Value, Mode>) => string' is not assignable to type '(token: TokenState<"breakpoint", Value, Mode>) => number | undefined'.

    Basically Typescript is unhappy that we return a number and a string. To fix the issue, there's 2 solutions:

    1. Return the same type:

    1. Excplicitely set the return type:

    Depending on the use case, you'll prefer the first over the second solution, or the second over the first.

    hashtag
    How to get the token value ?

    Now that we know how to get the TokenState that we want, we want to extract the value from it. As mentioned before, a token can have a lot of places that can contains an alias, so when retrieving the value, there’s 2 ways of doing it.

    hashtag
    Ignoring aliasing

    If you only want to get the raw value of a token and ignore the aliases, you can use the following function:

    Also, as combining matchByType and getJSONValue can become a bit heavy. For that case, you can use the following function:

    hashtag
    Handling the aliases

    hashtag
    Overview of the API

    If you want to support the aliases in your output, you’ll need to handle them when they are some at the top level, mode level, or value level. To do so, we provide the Stateful Value API that helps you to make sure you handle all the possible cases. Let’s see an example and then break down everything.

    Although this API is a bit verbose, it’ll make sure that you cannot miss a case. So, what’s happening?

    When you retrieve the value of a token, we put it inside a StatefulResult , and it’ll expose the following type union at each level:

    • Top level: ResolvableTopLevelAlias | UnresolvableTopLevelAlias | TopLevelValue

    • Mode level: ResolvableModeLevelAlias | UnresolvableModeLevelAlias | RawValueSignature

    • Value level: ResolvableValueLevelAlias | UnresolvableValueLevelAlias | string | number | ...

    Each mapping function is dedicated to handling one case.

    First, you’ll need to handle the top level:

    Note the unwrap at the end which is the way to extract the value.

    Then you’ll want to handle the mode level:

    All you need to do for the mode level is to return a value, and it'll be mapped to the right mode.

    And finally the value level (we take a dimension token as an example):

    circle-exclamation

    If you call mapPrimitiveValue, it needs to be the first one to be called in order to avoid remapping results produced by mapUnresolvableValueLevelAlias or mapResolvableValueLevelAlias.

    As you can see each step is always the same: handling unresolvable alias, resolvable alias and the value.

    hashtag
    Resolving an alias

    Sometimes, you need to use the precision of the stateful result API, but don't want to deal with the aliases at some levels. To avoid doing so, you can resolve an alias by calling resolveDeepValue, and you'll be left with handling the unresolvable alias and value case. Here is an example:

    hashtag
    Handling only the value case

    Although we could get rid of the resolvable alias case, handling the unresolvable case might still be too verbose. So let me introduce you to a nice pattern to only deal with values:

    1. We will check if the token is fully resolvable

    2. We'll use resolveDeepValue to remove the need of handling the resolvable aliases

    3. We'll use unwrapValue to remove the need of handling the unresolvable case at the type level

    Thanks to this pattern, we went from a really verbose API to something lighter.

    SDTFClient
    | { collection: string | true }
    | { group: string | true }
    | { token: string | true }
    | SDTFQuery
    { token: true }
    | 'hex' 
    | 'rgb' 
    | 'hsl' 
    | 'hsb' 
    | 'lch' 
    | 'lab'
    // Fetch a Repository
    const SPECIFY_REPOSITORY_NAME = "MY-REPOSITORY-NAME";
    const sdtfClient = await specifyClient.getSDTFClientByRepositoryName(
      SPECIFY_REPOSITORY_NAME,
    );
    
    console.log(`Fetched repository: ${sdtfClient.repository.name}`);
    const jsonTokenTree = sdtfClient.getJSONTokenTree();
    
    console.log(jsonTokenTree) // the object literal representation of the Specify Design Token data
    const tokenState = sdtfClient.getTokenState(['path', 'to', 'token'])
    const tokens = sdtfClient.getAllTokenStates()
    const groups = sdtfClient.getAllGroupStates()
    const collections = sdtfClient.getAllCollectionStates()
    const results = sdtfClient
      .mapQueryResults(
        { where: { collection: '^My Collection$', select: true, children: { tokens: true } } },
        (treeNodeState, engine, queryResult) => {
          if (treeNodeState.isCollection) {
            return treeNodeState.name
          }
          
          if (treeNodeState.isToken) {
            return treeNodeState.stringPath
          }
          
          return undefined
        },
      );
    sdtfClient
      .forEachQueryResult(
        { where: { token: '.*', select: true } },
        (treeNodeState, engine, queryResult) => {
          if (treeNodeState.isToken) {
            engine.mutation.renameToken({
              atPath: treeNodeState.path,
              name: treeNodeState.name.toLowerCase(),
            });
          }
        },
      );
    {
      mySize: {
        $type: 'dimension',
        $value: {
          small: { unit: 'px', value: 12 },
          large: { unit: 'px', value: 24 },
        }
      }
    }
    {
      sizeValue: {
        $type: 'number',
        $value: {
          default: 12
        }
      },
      mySize: {
        $type: 'dimension',
        $value: {
          small: {
    	unit: 'px',
    	value: { $alias: 'sizeValue', $mode: 'default' }
          },
          large: {
    	unit: 'px',
    	value: 24
          },
        }
      }
    }
    {
      anotherSize: {
        $type: 'dimension',
        $value: {
          customMode: {
    	unit: 'px',
    	value: 12
          }
        }
      },
      mySize: {
        $type: 'dimension',
        $value: {
          small: { $alias: 'anotherSize', $mode: 'customMode' },
          large: {
    	unit: 'px',
    	value: 24
          },
        }
      }
    }
    {
      anotherSize: {
        $type: 'dimension',
        $value: {
          small: {
    	unit: 'px',
    	value: 12
           },
           large: {
             unit: 'px',
    	 value: 24
           },
        }
      },
      mySize: {
        $type: 'dimension',
        $value: { $alias: 'anotherSize' },
      }
    }
    console.log('Token type:', tokenState.type) // Token type: <type>
    tokenState.matchByType(
      {
        // TokenState<'dimension'>
        dimension: (v) => { ... },
        // TokenState<'font'>
        font: (v) => { ... },
        // TokenState<'textStyle'>
        textStyle: (v) => { ... },
      },
      notMatched => { ... }
    )
    tokenState.matchByType(
      {
        dimension: _ => 1,
        breakpoint: _ => 'hello',
      },
      _ => undefined,
    )
    tokenState.matchByType(
      {
        dimension: _ => 1.toString(),
        breakpoint: _ => 'hello',
      },
      _ => undefined,
    )
    tokenState.matchByType<string | number>(
      {
        dimension: _ => 1,
        breakpoint: _ => 'hello',
      },
      _ => undefined,
    )
    tokenState.getJSONValue({
      resolveAliases: true,
      allowUnresolvable: false,
      targetMode: tokenState.modes[0],
    })
    tokenState.matchJSONValueByType(
      {
        // { unit: 'px', value: 12 }
        dimension: (v, mode) => { ... },
        // { family: 'MyFont', weight: 700, ... }
        font: (v, mode) => { ... },
        // { lineHeight: { unit: 'px', value: 24 }, ... }
        textStyle: (v, mode) => { ... },
      },
      notMatched => { ... }
    )
    // TokenState<'dimension'> -> { value: 12, unit: 'px' }
    tokenState
      .getStatefulValueResult()
      .mapResolvableTopLevelAlias(alias => { ... })
      .mapUnresolvableTopLevelAlias(alias => { ... })
      .mapTopLevelValue(modeLevel =>
        modeLevel
          .mapResolvableModeLevelAlias((alias, mode) => { ... })
          .mapUnresolvableModeLevelAlias(_ => { ... })
          .mapRawValue((rawValue, mode) => 
            rawValue
    	  .value
    	  .mapPrimitiveValue(value => { ... })
    	  .mapResolvableValueLevelAlias(alias => { ... })
    	  .mapUnresolvableValueLevelAlias(alias => { ... })				
          )
          .unwrap(),
      )
      .unwrap();
    
    tokenState
      .getStatefulValueResult()
      .mapResolvableTopLevelAlias(alias => { ... })
      .mapUnresolvableTopLevelAlias(alias => { ... })
      .mapTopLevelValue(modeLevel => { ... })
      .unwrap()
    .mapTopLevelValue(modeLevel =>
      modeLevel
        .mapResolvableModeLevelAlias((alias, mode) => { ... })
        .mapUnresolvableModeLevelAlias(_ => { ... })
        .mapRawValue((rawValue, mode) => { ... })
        .unwrap()
    .mapRawValue((dimension, mode) => {
      const value = dimension
        .value
        // mapPrimitiveValue is not required if you only need the value inside
        // without updating it
        .mapPrimitiveValue(value => { ... })
        .mapUnresolvableValueLevelAlias(alias => { ... })
        .mapResolvableValueLevelAlias(alias => { ... })
        .unwrap()
        
        return value
    })
    tokenState
      .getStatefulValueResult()
      .resolveDeepValue()
      .mapUnresolvableTopLevelAlias(alias => ...)
      .mapTopLevelValue(modeLevel => 
        modeLevel
          .resolveDeepValue()
          .mapUnresolvableModeLevelAlias(alias => ...)
          .mapRawValue((dimension, mode) => 
            dimension
              .value
              .resolveDeepValue()
              .mapUnresolvableValueLevelAlias(alias => ...)
              .unwrap()
          )
          .unwrap()
      )
      .unwrap()
    if (!tokenState.isFullyResolvable) {
      return
    }
    
    const output = tokenState
      .getStatefulValueResult()
      .resolveDeepValue()
      .mapTopLevelValue(modeLevel => 
        modeLevel
          .resolveDeepValue()
          .mapRawValue((dimension, mode) => 
            dimension
              .value
              .resolveDeepValue()
              .unwrapValue()
          )
          .unwrapValue()
      )
      .unwrapValue()
    circle-exclamation

    The SDTF is a transport format, if it can be read easily, we do not recommend writing its content directly. Instead, use the APIs provided by Specify.

    hashtag
    JSON Tree Structure

    With the SDTF, you organize design tokens as a regular JSON tree with some remarkable signatures such as token, group and collection.

    With groups and collections, you create sets of tokens to convey meaning like color.primary where color is the group and where my primary is a token, nested in color.

    There is no technical limitation to the level of nesting you can achieve, yet good practices would drive the usage to a couple or so.

    chevron-rightExample with dimensions in a grouphashtag
    const dimensionTokens: SpecifyDesignTokenFormat = {
      spacing
    
    chevron-rightExample with colors in a collectionhashtag
    const tokens: SpecifyDesignTokenFormat = {
      theme
    

    hashtag
    Tree node (token, group, collection)

    A tree node is any of token, group or collection that live within a SDTF token tree.

    hashtag
    Naming

    Token, group and collection names are free to use any character except the following:

    • . (dot/period) used for aliasing

    • $ reserved for SDTF

    hashtag
    Common properties

    Any group, collection or design token can hold extra properties:

    • $description a string aimed to complement the name.

    • $extensions a JSON object to complement the value for a given provider.

    chevron-rightExample with dimensionshashtag

    hashtag
    Note on $extensions

    The key used to index the $extensions object in $extensions: { [key: string]: JSONValue }MUST follow the reverse domain name notationarrow-up-right in order to ensure collision-free collaboration of actors.

    hashtag
    Tree path

    The tree path is the unique identifier of a tree node within the token tree. It is formed from the enumeration of the node's parent names.

    In the following example, the token base is nested into the spacing group. Thus, its path is ['spacing', 'base'] or "spacing.base".

    hashtag
    Representations

    Among the Specify APIs, you'll find 2 path representations.

    • the string based representation concatenates the path with . (dot/period) . Mainly used for aliasing. Giving for our previous example "spacing.base".

    • the array based representation with an array of string, where each item is the name of a tree node. Mainly used for working with the SDTF Engine. Giving for our previous example ['spacing', 'base'].

    hashtag
    Design token

    A design token represents the value of a design decision for all the possible modes you want to attach. It is defined by its $type and its corresponding $value, where the $value is a JSON object, Record<mode: string, value: any>, where the mode name is the key and holds to the actual value.

    A design token can be placed anywhere within a group or a collection.

    hashtag
    Token value

    hashtag
    Value defines at least one mode

    Any Token Value MUST define at least one mode.

    Default mode, by convention, is: default such as $value: { default: ... }

    hashtag
    Modes naming

    Modes names are free to use any character except the following:

    • . (dot/period) used for aliasing

    • $ reserved for SDTF

    hashtag
    Modes outside a collection

    A token placed outside of a collection can define any mode as far as it defines at least one mode.

    hashtag
    Modes inside a collection

    A token placed inside of a collection must strictly define the modes allowed by the collection.

    hashtag
    Aliasing (reference)

    Within a design token value, we can declare { $alias: 'path.to.token' } to reference another token, thus avoiding information duplication.

    You can alias tokens through three levels.

    hashtag
    Top-level aliasing

    You can alias an entire Token. Hence you also inherit from its modes.

    Note that $mode does not apply here.

    hashtag
    Mode-level aliasing

    You can alias on one or many modes of a Token. The value resolution will follow the indicated target $mode.

    Note that $mode is mandatory

    hashtag
    Value-level aliasing

    You can alias on (almost) all sub-values composing the SDTF. The value resolution will follow the indicated target $mode.

    Note that $mode is mandatory

    hashtag
    Aliasing validation

    Aliases must reference compatible Tokens. Each type has a unique mapping to other types defining which ones can be referenced at which place.

    chevron-rightExample of invalid aliasinghashtag

    Here is a dimension using value level aliasing.

    1. The Token expected to resolve for { $alias: 'a.value' } must be a number.

    2. But since number is a broad definition, we would also accept integerNumber, zeroToOneNumber, arcDegreeNumber, rgbColorNumber, positiveNumber, positiveIntegerNumber.

    3. Following the rationale, the Token expected to resolve for { $alias: 'a.unit' } must be a dimensionUnit.

    hashtag
    Supported types

    The SDTF defines a large variety of token types in order to capture (almost) any design decision.

    • bitmap

    • blur

    • border

    • breakpoint

    • color

    • dimension

    • duration

    • font

    • gradient

    • gradients

    • opacity

    • radius

    • radii

    • shadow

    • shadows

    • spacing

    • spacings

    • textStyle

    • transition

    • vector

    • zIndex

    chevron-rightUI type helpershashtag
    • bitmapFormat

    • borderStyle

    • borderStyleLineCap

    • cubicBezier

    • dimensionUnit

    • durationUnit

    • fontFamily

    • fontFeature

    • fontFeatures

    • fontFormat

    • fontStyle

    • fontWeight

    • shadowType

    • stepsTimingFunction

    • textAlignHorizontal

    • textAlignVertical

    • textDecoration

    • textTransform

    • vectorFormat

    chevron-rightJSON inherited typeshashtag
    • string

    • number

    • boolean

    • null

    • array

    • object

    chevron-rightConstrained primitiveshashtag
    • integerNumber

    • zeroToOneNumber

    • arcDegreeNumber

    • rgbColorNumber

    • positiveNumber

    • positiveIntegerNumber

    • percentageNumber

    • hexadecimalColorString

    hashtag
    Group

    A group is meant to order and nest design tokens semantically, it can be placed in any parent group or collection.

    A group is a simple JSON object that can contain none to many groups, collections or tokens. With no mandatory keys or children, a group can be empty and represented as an empty object {}.

    hashtag
    Collection

    A collection is a special kind of group defining a mandatory $collection property. It constrain any enclosed token to respect a given set of modes.

    A Collection can be placed within any group, nested at any level. But a Collection cannot be nested within another Collection.

    hashtag
    $collection property

    The $collection property is an object strictly containing the $modes key.

    The $modes property is an array of mode names (string) defining the allowed modes for any token within the collection.

    Parameter
    Required
    Type
    Default
    Description

    change

    false

    'names'

    Change the names or the modes of the selected items.

    hashtag
    Basic usage

    This example helps you transform in kebabCase the name all collections, groups, tokens and modes. Use this example if you want to generate CSS Custom properties with the to-css-custom-properties parser.

    We change the case of the token names and the modes to kebabCase. We applyTo the collection level so we transform in kebabCase:

    • the collection names

    • the group names

    • the token names

    • the mode names

    We eventually generate our transformed SDTF graph in a JSON file thanks to the parser.

    hashtag
    Overview
    Specify CLI
    Specify SDK

    Running parsers remotely

    ✅

    ✅

    Running parsers locally

    ❌

    ✅

    hashtag
    Setup

    hashtag
    CLI

    In the case of the CLI, you'll first need to install the CLI:

    npm install @specifyapp/cli 
    yarn global add @specifyapp/cli

    And then create a .specifyrc.json :

    hashtag
    SDK

    For the SDK, you'll first want to install the dependencies of the SDK, and the SDTF:

    Then you, you have to create a file where we'll import all the installed dependencies:

    hashtag
    Running a parser

    hashtag
    CLI

    We first need to fill up a bit the configuration file with the parser we want to run:

    Then we can run everything through the CLI:

    hashtag
    SDK

    On the SDK side, there's two way of doing it.

    hashtag
    Running parsers locally

    The main interest of the SDK is to be able to run everything locally. To do so, you can do the following:

    There's actually a way to choose which parser runs locally, and which one runs remotely, you can learn more about it here.

    hashtag
    Running parsers remotely

    Running parsers remotely is basically wrapping the configuration into the SDK:

    hashtag
    Filtering the tokens

    hashtag
    CLI

    To do so, we'll need to introduce a new parser in the configuration file:

    hashtag
    SDK

    Because the SDK is loading the SDTF, we can work directly with it. No need to run a parser, we can just execute filtering methods, and then generate our tokens:

    hashtag
    Updating tokens

    hashtag
    CLI

    Just like before, it can be done through a parser:

    hashtag
    SDK

    Again, as the SDK is loading the SDTF, we can work directly with it through the update method and an updater:

    hashtag
    Converting a token to a specific format

    hashtag
    CLI

    This cannot be done with the CLI.

    hashtag
    SDK

    Here is an example of converting a token to CSS, more detals on the conversion can be found here.

    CSS
    Swift
    Typescript
    SDK
    Specify CLI
    Specify SDK

    CSS

    To convert a token to CSS, you can import all the functions under the CSS namespace: @specifyapp/sdk/css

    hashtag
    Primitives tokens

    Here is the list of the primitives tokens for the CSS parser with an example of their respective output type.

    hashtag
    Border

    Input:

    Output:

    hashtag
    Breakpoint

    Input:

    Output:

    hashtag
    Color

    hashtag
    RGB

    Input:

    Output:

    hashtag
    HEX

    Input:

    Output:

    hashtag
    HSB

    Input:

    Output:

    hashtag
    HSL

    Input:

    Output:

    hashtag
    LAB

    Input:

    Output:

    hashtag
    LCH

    Input:

    Output:

    hashtag
    Cubic bezier

    Input:

    Output:

    hashtag
    Dimension

    Input:

    Output:

    hashtag
    Duration

    Input:

    Output:

    hashtag
    Font weight

    Input:

    Output:

    hashtag
    Gradient

    hashtag
    Conic

    Input:

    Output:

    hashtag
    Linear

    Input:

    Output:

    hashtag
    Radial

    Input:

    Output:

    hashtag
    Gradients

    Input:

    Output:

    hashtag
    Opacity

    Input:

    Output:

    hashtag
    Radii

    Input:

    Output:

    hashtag
    Radius

    Input:

    Output:

    hashtag
    Shadow

    Input:

    Output:

    hashtag
    Shadows

    Input:

    Output:

    hashtag
    Spacing

    Input:

    Output:

    hashtag
    Spacings

    Input:

    Output:

    hashtag
    Steps timing function

    Input:

    Output:

    hashtag
    Z-index

    Input:

    Output:

    hashtag
    Composites tokens

    Here is the list of the composites tokens for the CSS parser with an example of their respective output type.

    hashtag
    Font

    Input:

    Output:

    hashtag
    Text style

    Input:

    Output:

    hashtag
    Transition

    Input:

    Output:

    CSS Custom Properties

    This template is dedicated for Web developers using CSS. It helps you generate all types of design tokens as CSS Custom Properties in their respective CSS file and it helps you to optimize your SVG's.

    This example uses 5 different parsers:

    • to target specific token types like colors and text styles

    • to convert our colors in HSL

    const dimensionTokens: SpecifyDesignTokenFormat = {
      spacing: {
        $description: "Spacings for the webapp",
        one: {
          $description: "default spacing",
          $type: "spacing",
          $value: {
            default: { unit: "px", value: 4 },
          },
        },
        two: {
          $description: "doubled spacing",
          $type: "spacing",
          $value: {
            default: { unit: "px", value: 8 },
          },
          $extensions: { "com.specifyapp.highlighted": true },
        },
      },
    };
    aDimension: {
      $type: 'dimension',
      $value: {
        default: {
          value: { $alias: 'a.value', $mode: "default" },
          unit: { $alias: 'a.unit', $mode: "default" },
        },
      },
    },
    const dimensionTokens: SpecifyDesignTokenFormat = {
      spacing: {
        base: {
          $type: "spacing",
          $value: { default: { unit: "px", value: 4 }},
        },
      },
    };
    const dimensionToken = {
      $type: 'dimension', // Mandatory
      $value: { // Mandatory
        default: { // Stands for "default mode", mode name can be any string not starting with "$"
          value: 1, unit: '%' // Must be a valid value or reference for the type
        }
      },
      $description: 'A dimension token', // Optional string
      $extensions: { 'com.specifyapp.data': true } // Optional Record<string, JSONValue>
    }
    aDimension: {
      $type: 'dimension',
      $value: { $alias: 'a.reference.to.a.dimension' },
    }
    aDimension: {
      $type: 'dimension',
      $value: {
        small: { $alias: 'a.reference.to.a.small.dimension', $mode: 'customMode' }, // this is the mode level alias
        large: { value: 1, unit: 'px' },
      },
    }
    aDimension: {
      $type: 'dimension',
      $value: {
        default: {
          value: { $alias: 'a.reference.to.a.number', $mode: 'small' },
          unit: { $alias: 'a.reference.to.a.dimensionUnit', $mode: 'web' }
        },
      },
    }
    const tokens = {
      spacing: { // Group name
        $description: 'All spacings for webapp', // Optional string
        $extensions: { 'com.specifyapp.data': true } // Optional Record<string, JSONValue>
        base: {
          // ... token data
        }
      }
    }
    color: {
      $collection: { $modes: ['light', 'dark'] }, // $collection indicates this group is a collection
      black: {
        $type: 'color',
        $value: {
          light: { model: 'hex', hex: '#000000', alpha: 1 },
          dark: { model: 'hex', hex: '#000000', alpha: 1 },
        }
      },
      aGroup: { $description: 'This group is empty but shows it is allowed here' }
    }
    {
      "Colors": {
        "$collection": { "$modes": ["Light", "Dark"] },
        "Core": {
          "blue 100": {
            "$type": "color",
            "$description": "token 1 aliased with n modes within collection within n groups",
            "$value": {
              "Light": {
                "red": 255,
                "blue": 255,
                "alpha": 1,
                "green": 255,
                "model": "rgb"
              },
              "Dark": {
                "red": 229,
                "blue": 29,
                "alpha": 1,
                "green": 29,
                "model": "rgb"
              }
            }
          },
          "blue 700": {
            "$type": "color",
            "$description": "token 2 aliased with n modes within collection within n groups",
            "$value": {
              "Light": {
                "red": 255,
                "blue": 255,
                "alpha": 1,
                "green": 200,
                "model": "rgb"
              },
              "Dark": {
                "red": 229,
                "blue": 0,
                "alpha": 1,
                "green": 0,
                "model": "rgb"
              }
            }
          }
        },
        "semantic": {
          "background": {
            "button": {
              "primary": {
                "hover": {
                  "$type": "color",
                  "$description": "alias token with n modes within collection within n groups",
                  "$value": {
                    "Dark": {
                      "$mode": "dark",
                      "$alias": "Colors.Core.blue 100"
                    },
                    "Light": {
                      "$mode": "light",
                      "$alias": "Colors.Core.blue 700"
                    }
                  }
                }
              }
            }
          }
        }  
      }
    }
    interface parser {
      name: 'change-case';
      options: {
        change?: 'names' | 'modes';
        toCase:
          | 'camelCase'
          | 'capitalCase'
          | 'constantCase'
          | 'kebabCase'
          | 'noCase'
          | 'pascalCase'
          | 'pascalSnakeCase'
          | 'pathCase'
          | 'sentenceCase'
          | 'snakeCase'
          | 'trainCase';
        applyTo:
          | { collection: string | true }
          | { group: string | true }
          | { token: string | true }
          | SDTFQuery;
      };
    }
    npm install -D @specifyapp/sdk1.0.0-beta.5 @specifyapp/specify-design-token-format
    yarn install -D @specifyapp/sdk @specifyapp/sdtf
    import { createSpecifyClient } from '@specifyapp/sdk';
    
    async function run() {
      const specifyClient = createSpecifyClient();
    
      await specifyClient.authenticate('<your-personal-access-token>');
      
      const sdtfClient = specifyClient.getSDTFClientByName('<repository-name>');
    }
    
    run()
    {
      "version": "2",
      "repository": "@organization/repository",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": []
    }
    {
      "version": "2",
      "repository": "@organization/repository",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Tokens as CSS Variables",
          "parsers": [
            {
              "name": "to-css-custom-properties",
              "output": {
                "type":"file",
                "filePath": "tokens.css"
              }
            }
          ]
        }
      ]
    }
    specify pull
    import { createSpecifyClient, createSpecifyFileSystemHelper } from '@specifyapp/sdk';
    import { toCssCustomProperties } from '@specifyapp/sdk/next';
    
    async function run() {
      const specifyClient = createSpecifyClient();
    
      await specifyClient.authenticate('<your-personal-access-token>');
      
      const sdtfClient = specifyClient.getSDTFClientByName('<repository-name>');
      
      const generatorPipelineOutput = await sdtfClient.createParserPipeline(
        toCssCustomProperties({ filePath: 'myFile.css' }),
      );
      
      const fileSystemHelper = createSpecifyFileSystemHelper({ generatorPipelineOutput });
      
      await fileSystemHelper.writeToDisk('./rootFolder');
    }
    
    run()
    import { createSpecifyClient, createSpecifyFileSystemHelper } from '@specifyapp/sdk';
    
    async function run() {
      const specifyClient = createSpecifyClient();
    
      await specifyClient.authenticate('<your-personal-access-token>');
      
      const sdtfClient = specifyClient.getSDTFClientByName('<repository-name>');
      
      const pipeEngineRuleResponseBody = await sdtfClient.transformWithRemoteParsers([
        {
          name: 'to-css-custom-properties',
          output: {type: 'file', filePath: 'tokens.css'},
        },
      ]);
      
      const fileSystemHelper = createSpecifyFileSystemHelper({
        pipeEngineRuleResponseBody
      });
      
      const writtenFiles = await fileSystemHelper.writeToDisk('./public');
    }
    
    run()
    {
      "version": "2",
      "repository": "@organization/repository",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Tokens as CSS Variables",
          "parsers": [
            {
              "name": "filter",
              "options": {
                "where": {
                  "collection": "colors",
                  "select": {
                    "collection": true,
                    "children": {
                      "tokens": true
                    }
                  }
                }
              }
            },
            {
              "name": "to-css-custom-properties",
              "output": {
                "type":"file",
                "filePath": "tokens.css"
              }
            }
          ]
        }
      ]
    }
    import { createSpecifyClient, createSpecifyFileSystemHelper } from '@specifyapp/sdk';
    import { toCssCustomProperties } from '@specifyapp/sdk/next';
    
    async function run() {
      const specifyClient = createSpecifyClient();
    
      await specifyClient.authenticate('<your-personal-access-token>');
      
      const sdtfClient = specifyClient.getSDTFClientByName('<repository-name>');
      
      const filteredGraph = sdtfClient.query({
        where: {
          collection: "colors",
          select: {
            collection: true,
            children: {
              tokens: true
            }
          }
        }
      });
      
      const generatorPipelineOutput = await filteredGraph
        .createParserPipeline(
          toCssCustomProperties({ filePath: 'myFile.css' }),
        )
        .execute();
      
      const fileSystemHelper = createSpecifyFileSystemHelper({ generatorPipelineOutput });
      
      await fileSystemHelper.writeToDisk('./rootFolder');
    }
    
    run()
    {
      "version": "2",
      "repository": "@organization/repository",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Tokens as CSS Variables",
          "parsers": [
            {
              "name": "convert-color",
              "options": {
                "toFormat": "hex"
              }
            },
            {
              "name": "to-css-custom-properties",
              "output": {
                "type":"file",
                "filePath": "tokens.css"
              }
            }
          ]
        }
      ]
    }
    import { createSpecifyClient, createSpecifyFileSystemHelper } from '@specifyapp/sdk';
    import { toCssCustomProperties, updaters } from '@specifyapp/sdk/next';
    
    async function run() {
      const specifyClient = createSpecifyClient();
    
      await specifyClient.authenticate('<your-personal-access-token>');
      
      const sdtfClient = specifyClient.getSDTFClientByName('<repository-name>');
      
      sdtfClient.update(
        updaters.color({ toFormat: 'hex' }, { where: { token: '^theme', select: true }})
      );
      
      const generatorPipelineOutput = await filteredGraph
        .createParserPipeline(
          toCssCustomProperties({ filePath: 'myFile.css' }),
        )
        .execute();
      
      const fileSystemHelper = createSpecifyFileSystemHelper({ generatorPipelineOutput });
      
      await fileSystemHelper.writeToDisk('./rootFolder');
    }
    
    run()
    import { createSpecifyClient } from '@specifyapp/sdk';
    import {
      dimensionToCss,
      breakpointToCss,
      colorToCss,
      createResolveAliasStrategy 
    } from '@specify/sdk/css'
    
    async function run() {
      const specifyClient = createSpecifyClient();
    
      await specifyClient.authenticate('<your-personal-access-token>');
      
      const sdtfClient = specifyClient.getSDTFClientByName('<repository-name>');
      
      const strategy = createResolveAliasStrategy()
    
      const outputs = sdtfClient.mapTokenStates(tokenState => 
        tokenState.matchByType(
          {
            dimension: dimensionToCss(strategy) // Output example: '12px'
            breakpoint: breakpointToCss(strategy) // Output example: '12px'
            color: colorToCss(strategy) // Output example: '#ff00ff'
          }, 
          _ => undefined
        )
      )
    }
    
    run()
    import { dimensionToCss, colorToCss, ... } from '@specifyapp/sdk/css';
    :
    {
    //
    spacing is a Group
    one: { // one is a Token
    $type: 'spacing',
    $value: {
    default: { unit: 'px', value: 4 }
    }
    },
    two: { // two is a Token
    $type: 'spacing',
    $value: {
    default: { unit: 'px', value: 8 }
    }
    }
    }
    }
    :
    {
    $collection: { $modes: ['light', 'dark'] },
    color: {
    blue: {
    $type: 'color',
    $value: {
    light: { model: 'hex', hex: '#1919C4', alpha: 1 },
    dark: { model: 'hex', hex: '#111146', alpha: 1 }
    }
    }
    }
    }
    }

    Update the tokens

    ✅

    ✅

    Filter the tokens

    ✅

    ✅

    Converting a token to a specific format

    ❌

    ✅

    Create a custom output

    ❌

    ✅

    toCase

    required

    The case transformation to apply. Actual transform is done by the change-casearrow-up-right package.

    applyTo

    required

    The selection where to apply the transformation. collection, group, token take a Regex string or true to select anything of the kind. An SDTFQuery can be used for advance use cases.

    to-sdtf
    {
      color: { model: 'hex', hex: '#ffffff', alpha: 0.5 },
      style: 'dashed',
      width: {
        value: 12,
        unit: 'px',
      },
      rectangleCornerRadii: null,
    }
    '12px dashed #ffffff80'
    {
      value: 12,
      unit: 'rem'
    }
    '12rem'
    {
      model: 'rgb',
      red: 12,
      green: 12,
      blue: 12,
      alpha: 0.5,
    }
    'rgba(12, 12, 12, 0.5)'
    {
      model: 'hsb',
      hue: 12,
      saturation: 12,
      brightness: 12,
      alpha: 0.1,
    }
    'hsva(12, 12%, 12%, 0.1)'
    {
      model: 'hex',
      hex: '#ff00ff',
      alpha: 0.5,
    }
    '#ff00ff80'
    {
      model: 'hsl',
      hue: 12,
      saturation: 12,
      lightness: 12,
      alpha: 0.5,
    }
    'hsla(12, 12%, 12%, 0.5)'
    {
      model: 'lab',
      bAxis: 12,
      aAxis: 12,
      lightness: 12,
      alpha: 0.5,
    }
    'lab(12% 12 12 / 0.5)'
    {
      model: 'lch',
      chroma: 12,
      hue: 12,
      lightness: 12,
      alpha: 1,
    }
    'lch(12% 12 12)'
    [0.12, 0.21, 0.12, 0.21]
    'cubic-bezier(0.12, 0.21, 0.12, 0.21)'
    {
      value: 12,
      unit: 'px'
    }
    '12px'
    {
      value: 1,
      unit: 's'
    }
    "1s"
    900
    "900'
    {
      type: 'conic',
      colorStops: [
        {
          color: {
            model: 'hex',
            hex: '#ffffff',
            alpha: 1,
          },
          position: 0,
        },
        {
          color: {
            model: 'hex',
            hex: '#ffff00',
            alpha: 1,
          },
          position: 1,
        },
      ],
      angle: 12,
      position: 'center',
    }
    'conic-gradient(from 12deg at center, #ffffff 0%, #ffff00 100%)'
    {
      type: 'linear',
      colorStops: [
        {
          color: {
            model: 'hex',
            hex: '#ffffff',
            alpha: 1,
          },
          position: 0,
        },
        {
          color: {
            model: 'hex',
            hex: '#ffff00',
            alpha: 1,
          },
          position: 1,
        },
      ],
      angle: 12,
    }
    'linear-gradient(12deg, #ffffff 0%, #ffff00 100%)'
    {
      type: 'radial',
      colorStops: [
        {
          color: {
            model: 'hex',
            hex: '#ffffff',
            alpha: 1,
          },
          position: 0,
        },
        {
          color: {
            model: 'hex',
            hex: '#ffff00',
            alpha: 1,
          },
          position: 1,
        },
      ],
      position: 'center',
    }
    'radial-gradient(circle at center, #ffffff 0%, #ffff00 100%)'
    [
      {
        type: 'conic',
        colorStops: [
          {
            color: {
              model: 'hex',
              hex: '#ffffff',
              alpha: 1,
            },
            position: 0,
          },
          {
            color: {
              model: 'hex',
              hex: '#ffff00',
              alpha: 1,
            },
            position: 1,
          },
        ],
        angle: 12,
        position: 'center',
      },
      {
        type: 'linear',
        colorStops: [
          {
            color: {
              model: 'hex',
              hex: '#ffffff',
              alpha: 1,
            },
            position: 0,
          },
          {
            color: {
              model: 'hex',
              hex: '#ffff00',
              alpha: 1,
            },
            position: 1,
          },
        ],
        angle: 12,
      },
    ]
    'conic-gradient(from 12deg at center, #ffffff 0%, #ffff00 100%), linear-gradient(12deg, #ffffff 0%, #ffff00 100%)'
    0.1
    '0.1'
    [
      { unit: 'px', value: 12 },
      { unit: '%', value: 24 },
    ]
    '12px 24%'
    { unit: 'px', value: 24 }
    '24px'
    {
      color: { model: 'hex', hex: '#ffffff', alpha: 1 },
      offsetX: { value: 12, unit: 'px' },
      offsetY: { value: 12, unit: 'px' },
      blurRadius: { value: 12, unit: 'px' },
      spreadRadius: { value: 12, unit: 'px' },
      type: 'inner',
    }
    'inset 12px 12px 12px 12px #ffffff'
    [
      {
        color: { model: 'hex', hex: '#ffffff', alpha: 1 },
        offsetX: { value: 12, unit: 'px' },
        offsetY: { value: 12, unit: 'px' },
        blurRadius: { value: 12, unit: 'px' },
        spreadRadius: { value: 12, unit: 'px' },
        type: 'inner',
      },
      {
        color: { model: 'hex', hex: '#ffffff', alpha: 1 },
        offsetX: { value: 12, unit: 'px' },
        offsetY: { value: 12, unit: 'px' },
        blurRadius: { value: 12, unit: 'px' },
        spreadRadius: { value: 12, unit: 'px' },
        type: 'inner',
      },
    ]
    'inset 12px 12px 12px 12px #ffffff, inset 12px 12px 12px 12px #ffffff'
    { unit: 'px', value: 24 }
    '24px'
    [
      { unit: 'px', value: 12 },
      { unit: '%', value: 24 },
      { unit: 'rem', value: 4 },
    ]
    '12px 24% 4rem'
    {
      stepsCount: 2,
      jumpTerm: 'start',
    }
    'steps(2, start)'
    10
    "10"
    {
      family: 'fontFamily',
      postScriptName: 'Postscript',
      files: [],
      weight: 'bold',
      style: 'italic',
    }
    {
      'font-style': 'italic',
      'font-family': "'Postscript'",
      'font-weight': 'bold',
    }
    {
      font: {
        family: 'textStyleFamily',
        postScriptName: 'Postscript',
        files: [],
        weight: 'bold',
        style: 'italic',
      },
      color: {
        model: 'hex',
        hex: '#ffffff',
        alpha: 0.5,
      },
      fontSize: { value: 12, unit: 'em' },
      fontFeatures: ['all-petite-caps'],
      lineHeight: { value: 12, unit: 'rem' },
      letterSpacing: { value: 12, unit: 'rem' },
      paragraphSpacing: { value: 12, unit: 'rem' },
      textAlignHorizontal: 'center',
      textAlignVertical: 'bottom',
      textDecoration: 'dashed',
      textIndent: { value: 12, unit: 'rem' },
      textTransform: 'capitalize',
    }
    {
      'font-style': 'italic',
      'font-family': "'Postscript'",
      'font-weight': 'bold',
      'font-size': '12em',
      color: '#ffffff80',
      'text-indent': '12rem',
      'line-height': '12rem',
      'letter-spacing': '12rem',
      'text-align': 'center',
      'text-transform': 'capitalize',
      'text-decoration': 'dashed',
    }
    {
      duration: {
        value: 12,
        unit: 's',
      },
      delay: {
        value: 10,
        unit: 'ms',
      },
      timingFunction: {
        jumpTerm: 'end',
        stepsCount: 2,
      },
    }
    {
      delay: '10ms',
      duration: '12s',
      'timing-function': 'steps(2, end)',
    }
    | 'camelCase'
    | 'capitalCase'
    | 'constantCase'
    | 'kebabCase'
    | 'noCase'
    | 'pascalCase'
    | 'pascalSnakeCase'
    | 'pathCase'
    | 'sentenceCase'
    | 'snakeCase'
    | 'trainCase'
    | { collection: string | true }
    | { group: string | true }
    | { token: string | true }
    | SDTFQuery
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Format all token names and modes to kebabCase and generate tokens in JSON",
          "parsers": [
            {
              "name": "change-case",
              "options": {
                "change": "names",
                "toCase": "kebabCase",
                "applyTo": {
                  "collection": true
                }
              }
            },
            {
              "name": "change-case",
              "options": {
                "change": "modes",
                "toCase": "kebabCase",
                "applyTo": {
                  "collection": true
                }
              }
            },
            {
              "name": "to-sdtf",
              "output": {
                "type": "file",
                "filePath": "tokens.json"
              }
            }
          ]
        }
      ]
    }
    tokens.json
    {
      "colors": {
        "$collection": { "$modes": ["light", "dark"] },
        "core": {
          "blue-100": {
            "$type": "color",
            "$description": "token 1 aliased with n modes within collection within n groups",
            "$value": {
              "light": {
                "red": 255,
                "blue": 255,
                "alpha": 1,
                "green": 255,
                "model": "rgb"
              },
              "dark": {
                "red": 229,
                "blue": 29,
                "alpha": 1,
                "green": 29,
                "model": "rgb"
              }
            }
          },
          "blue-700": {
            "$type": "color",
            "$description": "token 2 aliased with n modes within collection within n groups",
            "$value": {
              "light": {
                "red": 255,
                "blue": 255,
                "alpha": 1,
                "green": 200,
                "model": "rgb"
              },
              "dark": {
                "red": 229,
                "blue": 0,
                "alpha": 1,
                "green": 0,
                "model": "rgb"
              }
            }
          }
        },
        "semantic": {
          "background": {
            "button": {
              "primary": {
                "hover": {
                  "$type": "color",
                  "$description": "alias token with n modes within collection within n groups",
                  "$value": {
                    "dark": {
                      "$mode": "dark",
                      "$alias": "colors.core.blue-100"
                    },
                    "light": {
                      "$mode": "light",
                      "$alias": "colors.core.blue-700"
                    }
                  }
                }
              }
            }
          }
        }  
      }
    }
    'names' | 'modes'

    change-case to change the name of our tokens and modes to kebabCase

  • to-css-custom-propertiesarrow-up-right to generate a CSS file containing our tokens

  • make-line-height-relative to make our text styles' line-height relative to their font-size

  • convert-dimension to convert our text styles' font-size from px to rem

  • to-css-text-style to generate our text styles as CSS classes

  • svgo to optimize, transform vectors, and generate SVG files

  • This template is an example among others. Head toward the to-css-custom-propertiesarrow-up-right page to get all available options.

    When using the CLI, you need to fill three properties:

    • repository is @organization/repository

    • personalAccessToken which you can generate in your account settingsarrow-up-right

    • rules lets you transform tokens by chaining parsers

    If you use the GitHub, you need to fill 4 properties:

    • repository is @organization/repository

    • head lets you set the branch your PR will be created on

    filterarrow-up-right
    convert-colorarrow-up-right

    base lets you set the branch your PR will be merged on

  • rules lets you transform tokens by chaining parsers

  • circle-info

    Make sure you have connected your GitHub account with your Specify account. Head toward this articlearrow-up-right to learn more.

    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Generate colors in HSL",
          "parsers": [
            {
              "name": "filter",
              "options": {
                "query": {
                  "where": {
                    "token": ".*",
                    "withTypes": {
                      "include": [
                        "color"
                      ]
                    },
                    "select": {
                      "parents": true
                    }
                  }
                }
              }
            },
            {
              "name": "convert-color",
              "options": {
                "toFormat": "hsl",
                "applyTo": {
                  "collection": true
                }
              }
            },
            {
              "name": "change-case",
              "options": {
                "change": "names",
                "toCase": "kebabCase",
                "applyTo": {
                  "collection": true
                }
              }
            },
            {
              "name": "change-case",
              "options": {
                "change": "modes",
                "toCase": "kebabCase",
                "applyTo": {
                  "collection": true
                }
              }
            },
            {
              "name": "to-css-custom-properties",
              "output": {
                "type": "file",
                "filePath": "output/colors.css"
              }
            }
          ]
        },
        {
          "name": "Generate text styles as CSS classes",
          "parsers": [
            {
              "name": "filter",
              "options": {
                "query": {
                  "where": {
                    "token": ".*",
                    "withTypes": {
                      "include": [
                        "textStyle"
                      ]
                    },
                    "select": {
                      "token": true
                    }
                  }
                }
              }
            },
            {
              "name": "change-case",
              "options": {
                "toCase": "kebabCase",
                "applyTo": {
                  "token": true
                }
              }
            },
            {
              "name": "make-line-height-relative",
              "options": {}
            },
            {
              "name": "convert-dimension",
              "options": {
                "applyToKeys": {
                  "textStyle": [
                    "fontSize"
                  ]
                },
                "toFormat": "rem"
              }
            },   
            {
              "name": "to-css-text-style",
              "output": {
                "type": "file",
                "filePath": "output/text-styles.scss"
              },
              "options": {
                "genericFamily": "sans-serif",
                "tokenNameTemplate": "text-style-{{token}}"
              }
            }
          ]
        },
        {
          "name": "Optimize and transform vectors with svgo + Generate SVG files",
          "parsers": [
            {
              "name": "svgo",
              "output": {
                "type": "directory",
                "directoryPath": "output/assets"
              },
              "options": {
                "svgo": {
                  "plugins": [
                    {
                      "name": "removeDimensions"
                    },
                    {
                      "name": "convertColors",
                      "params": {
                        "currentColor": true
                      }
                    }
                  ]
                }
              }
            }
          ]
        }
      ]
    }
    .specifyrc.json
    {
      "version": "2",
      "head": "specifyrc-json",
      "base": "main",
      "repository": "@organization/repository",
      "rules": [
        {
          "name": "Generate colors in HSL",
          "parsers": [
            {
              "name": "filter",
              "options": {
                "query": {
                  "where": {
                    "token": ".*",
                    "withTypes": {
                      "include": [
                        "color"
                      ]
                    },
                    "select": {
                      "parents": true
                    }
                  }
                }
              }
            },
            {
              "name": "convert-color",
              "options": {
                "toFormat": "hsl",
                "applyTo": {
                  "collection": true
                }
              }
            },
            {
              "name": "change-case",
              "options": {
                "change": "names",
                "toCase": "kebabCase",
                "applyTo": {
                  "collection": true
                }
              }
            },
            {
              "name": "change-case",
              "options": {
                "change": "modes",
                "toCase": "kebabCase",
                "applyTo": {
                  "collection": true
                }
              }
            },
            {
              "name": "to-css-custom-properties",
              "output": {
                "type": "file",
                "filePath": "tokens.css"
              }
            }
          ]
        },
        {
          "name": "Generate text styles as CSS classes",
          "parsers": [
            {
              "name": "filter",
              "options": {
                "query": {
                  "where": {
                    "token": ".*",
                    "withTypes": {
                      "include": [
                        "textStyle"
                      ]
                    },
                    "select": {
                      "token": true
                    }
                  }
                }
              }
            },
            {
              "name": "change-case",
              "options": {
                "toCase": "kebabCase",
                "applyTo": {
                  "token": true
                }
              }
            },
            {
              "name": "make-line-height-relative",
              "options": {}
            },
            {
              "name": "convert-dimension",
              "options": {
                "applyToKeys": {
                  "textStyle": [
                    "fontSize"
                  ]
                },
                "toFormat": "rem"
              }
            },   
            {
              "name": "to-css-text-style",
              "output": {
                "type": "file",
                "filePath": "public/text-styles.scss"
              },
              "options": {
                "genericFamily": "sans-serif",
                "tokenNameTemplate": "text-style-{{token}}"
              }
            }
          ]
        },
        {
          "name": "Optimize and transform vectors with svgo + Generate SVG files",
          "parsers": [
            {
              "name": "svgo",
              "output": {
                "type": "directory",
                "directoryPath": "output/assets"
              },
              "options": {
                "svgo": {
                  "plugins": [
                    {
                      "name": "removeDimensions"
                    },
                    {
                      "name": "convertColors",
                      "params": {
                        "currentColor": true
                      }
                    }
                  ]
                }
              }
            }
          ]
        }
      ]
    }

    convert-dimension

    This parser helps you convert units of dimension tokens (spacing, sizing, breakpoint, blur...) and composite tokens sub values (font-size, letter-spacing, border-width...)

    hashtag
    Interface

    hashtag
    Options

    Parameter
    Required
    Type
    Default
    Description

    toFormat

    required

    The target dimension format to convert to.

    hashtag
    Examples

    hashtag
    Basic usage

    We convert all dimensions from our SDTF graph in rem.

    We then generate our transformed SDTF graph in a JSON file thanks to the to-sdtf parser.

    hashtag
    Generate unitless dimension tokens

    hashtag
    Convert text style's font-size from px to rem

    Configuration file 101

    Learn more about how to setup your Specify configuration file to generate design tokens and assets fitting your company standards.

    hashtag
    Introduction

    Whenever you want to work with the or the destination, you need to create a Configuration file to instruct the how to transform your design data, so it generate tokens that match your technical requirements.

    A configuration file helps you:

    {
      "Foundation": {
        "spacing": {
          "1": {
            "$type": "dimension",
            "$value": {
              "default": {
                "value": 4,
                "unit": "px"
              }
            }
          },
          "2": {
            "$type": "dimension",
            "$value": {
              "default": {
                "value": 8,
                "unit": "px"
              }
            }
          },
          "3": {
            "$type": "dimension",
            "$value": {
              "default": {
                "value": 12,
                "unit": "px"
              }
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Convert spacing in rem and generate tokens in JSON",
          "parsers": [
            {
              "name": "convert-dimension",
              "options": {
                "toFormat": "rem"
              }
            },
            {
              "name": "to-sdtf",
              "output": {
                "type": "file",
                "filePath": "tokens.json"
              }
            }
          ]
        }
      ]
    }
    tokens.json
    {
      "Foundation": {
        "spacing": {
          "1": {
            "$type": "dimension",
            "$value": {
              "default": {
                "value": 0.25,
                "unit": "rem"
              }
            }
          },
          "2": {
            "$type": "dimension",
            "$value": {
              "default": {
                "value": 0.5,
                "unit": "rem"
              }
            }
          },
          "3": {
            "$type": "dimension",
            "$value": {
              "default": {
                "value": 0.75,
                "unit": "rem"
              }
            }
          }
        }
      }
    }
    {
      "Foundation": {
        "spacing": {
          "1": {
            "$type": "dimension",
            "$value": {
              "default": {
                "value": 4,
                "unit": "px"
              }
            }
          },
          "2": {
            "$type": "dimension",
            "$value": {
              "default": {
                "value": 8,
                "unit": "px"
              }
            }
          },
          "3": {
            "$type": "dimension",
            "$value": {
              "default": {
                "value": 12,
                "unit": "px"
              }
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Convert spacing in rem and generate tokens in JSON",
          "parsers": [
            {
              "name": "convert-dimension",
              "options": {
                "toFormat": null
              }
            },
            {
              "name": "to-sdtf",
              "output": {
                "type": "file",
                "filePath": "tokens.json"
              }
            }
          ]
        }
      ]
    }
    tokens.json
    {
      "Foundation": {
        "spacing": {
          "1": {
            "$type": "dimension",
            "$value": {
              "default": {
                "value": 0.25,
                "unit": null
              }
            }
          },
          "2": {
            "$type": "dimension",
            "$value": {
              "default": {
                "value": 0.5,
                "unit": null
              }
            }
          },
          "3": {
            "$type": "dimension",
            "$value": {
              "default": {
                "value": 0.75,
                "unit": null
              }
            }
          }
        }
      }
    }
    {
      "Text styles": {
        "Body": {
          "$type": "textStyle",
          "$value": {
            "default": {
              "font": {
                "family": "Neue Haas Grotesk Text Pro",
                "postScriptName": "Neue Haas Grotesk Text Pro",
                "weight": "roman",
                "style": "normal",
                "files": []
              },
              "fontSize": {
                "value": 14,
                "unit": "px"
              },
              "color": null,
              "fontFeatures": null,
              "lineHeight": {
                "value": 150,
                "unit": "%"
              },
              "letterSpacing": {
                "value": 0.2,
                "unit": "px"
              },
              "paragraphSpacing": {
                "value": 0,
                "unit": "px"
              },
              "textAlignHorizontal": null,
              "textAlignVertical": null,
              "textDecoration": "none",
              "textIndent": {
                "value": 0,
                "unit": "px"
              },
              "textTransform": "none"
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Convert font-sizes to rem",
          "parsers": [
            {
              "name": "convert-dimension",
              "options": {
                "applyToKeys": {
                  "textStyle": [
                    "fontSize"
                  ]
                },
                "toFormat": "rem"
              }
            },
            {
              "name": "to-sdtf",
              "output": {
                "type": "file",
                "filePath": "public/tokens.json"
              }
            }
          ]
        }
      ]
    }
    tokens.json
    {
      "Text styles": {
        "Body": {
          "$type": "textStyle",
          "$value": {
            "default": {
              "font": {
                "family": "Neue Haas Grotesk Text Pro",
                "postScriptName": "Neue Haas Grotesk Text Pro",
                "weight": "roman",
                "style": "normal",
                "files": []
              },
              "fontSize": {
                "value": 0.875,
                "unit": "rem"
              },
              "color": null,
              "fontFeatures": null,
              "lineHeight": {
                "value": 150,
                "unit": "%"
              },
              "letterSpacing": {
                "value": 0.2,
                "unit": "px"
              },
              "paragraphSpacing": {
                "value": 0,
                "unit": "px"
              },
              "textAlignHorizontal": null,
              "textAlignVertical": null,
              "textDecoration": "none",
              "textIndent": {
                "value": 0,
                "unit": "px"
              },
              "textTransform": "none"
            }
          }
        }
      }
    }
    interface parser {
      name: 'convert-dimension';
      options: {
        toFormat:
          | '%' 
          | 'px' 
          | 'em' 
          | 'rem' 
          | 'pt' 
          | 'pc' 
          | 'in' 
          | 'cm' 
          | 'mm' 
          | 'ex' 
          | 'cap' 
          | 'ch' 
          | 'ic' 
          | 'lh' 
          | 'rlh' 
          | 'vw' 
          | 'svw' 
          | 'lvw' 
          | 'dvw' 
          | 'vh' 
          | 'svh' 
          | 'lvh' 
          | 'dvh' 
          | 'vi' 
          | 'svi' 
          | 'lvi' 
          | 'dvi' 
          | 'vb' 
          | 'svb' 
          | 'lvb' 
          | 'dvb' 
          | 'vmin' 
          | 'svmin' 
          | 'lvmin' 
          | 'dvmin' 
          | 'vmax' 
          | 'svmax' 
          | 'lvmax' 
          | 'dvmax'
          | null;
        baseValue?: {
          rem?: number
        };
        applyTo?: SDTFQuery;
        applyToKeys?: {
          textStyle?: Array<'fontSize' | 'lineHeight' | 'letterSpacing' | 'paragraphSpacing'| 'textIndent'>,
          shadow?: Array<'offsetX' | 'offsetY' | 'blurRadius' | 'spreadRadius'>,
          border?: Array<'width' | 'rectangleCornerRadii'>,
        };
        excludeFormats?: array;
        includeFormats?: array;
      };
    }

    baseValue

    false

    applyTo

    false

    The selection where to apply the transformation. collection, group, token take a Regex string or true to select anything of the kind. An SDTFQuery can be used for advance use cases.

    applyToKeys

    false

    Select composite tokens sub-values to convert

    excludeFormats

    false

    array

    List of dimension formats you don't want to convert.

    includeFormats

    false

    array

    List of dimension formats you only want to convert.

    { rem: number }
    | { collection: string | true }
    | { group: string | true }
    | { token: string | true }
    | SDTFQuery
    { token: true }
    {
      textStyle?: Array<'fontSize' | 'lineHeight' | 'letterSpacing' | 'paragraphSpacing'| 'textIndent'>,
      shadow?: Array<'offsetX' | 'offsetY' | 'blurRadius' | 'spreadRadius'>,
      border?: Array<'width' | 'rectangleCornerRadii'>,
    }
    | '%' 
    | 'px' 
    | 'em' 
    | 'rem' 
    | 'pt' 
    | 'pc' 
    | 'in' 
    | 'cm' 
    | 'mm' 
    | 'ex' 
    | 'cap' 
    | 'ch' 
    | 'ic' 
    | 'lh' 
    | 'rlh' 
    | 'vw' 
    | 'svw' 
    | 'lvw' 
    | 'dvw' 
    | 'vh' 
    | 'svh' 
    | 'lvh' 
    | 'dvh' 
    | 'vi' 
    | 'svi' 
    | 'lvi' 
    | 'dvi' 
    | 'vb' 
    | 'svb' 
    | 'lvb' 
    | 'dvb' 
    | 'vmin' 
    | 'svmin' 
    | 'lvmin' 
    | 'dvmin' 
    | 'vmax' 
    | 'svmax' 
    | 'lvmax' 
    | 'dvmax'
    | null

    request design tokens and assets from a Specify repository

  • transform them to fit your company standards thanks to parsers

  • hashtag
    Properties

    A configuration is composed of 3 main properties:

    • repository

    • personalAccessToken

    • rules

    hashtag
    Repository

    The name of the Specify repository you want to pull your design tokens and assets from.

    Let's say we have the following repository in Specify called "all-design-data" located in the "@acme-inc" organization.

    An example Specify repository called "all-design-data-v2" located in the "@acme-inc" organization.

    We target it like this:

    circle-info

    You can only target one repository per configuration file. Want to pull design tokens from several Specify repositories? Create several configuration files and run them with the Specify CLI (See example scriptarrow-up-right).

    hashtag
    Personal Access Token

    The Specify personalAccessToken used to authenticate you.

    circle-info

    Need a personal access token? Generate one ↗arrow-up-right

    hashtag
    Parser Rules

    The Parsers Rules help you transform your design tokens and assets the way you want.

    You can have as many rules as you want and you can have rules that transform several Token types at once.

    circle-info

    Looking for the rules configuration? 👉 review the Parser Rule reference.

    Here are different kind of rules and parsers you can use to generate color tokens as CSS Custom Properties:

    1. filterarrow-up-right to target on a specific collection named "Colors" that contains our colors

    2. convert-colorarrow-up-right to convert our colors in HSL

    3. change-case to change the name of our tokens and modes to kebabCase

    4. to generate a CSS file containing our tokens

    hashtag
    Examples

    hashtag
    How to run these examples

    The following examples are made to be used with the Specify CLI.

    Requirements:

    • a Specify repository containing design tokens

    • a valid personal access token (Generate one ↗arrow-up-right)

    Run all examples by copying the code and running the specify pull command.

    hashtag
    Basic

    Here's a basic configuration file that targets a Specify repository called design-system from the @acme-inc organization:

    This example config file will return a colors.css file containing all design tokens stored in the design-system repository.

    Here's an example of a token value returned by Specify:

    hashtag
    Pull colors as CSS Custom Properties

    Now let's update our previous configuration to only pull colors and transform them as CSS Custom Properties in RGB.

    Here is the input returned by Specify and the output generated by Specify after executing our configuration.

    GitHub
    Specify CLI
    Parsers Engine

    to-tailwind

    This parser helps you generate a Tailwind theme from all your design tokens coming from Specify.

    hashtag
    Interface

    hashtag
    Options

    .specifyrc.js
    module.exports = {
      version: '2'
      repository: '@acme-inc/all-design-data-v2',
      personalAccessToken: '<your-personal-access-token>',
      rules: [],
    };
    .specifyrc.json
    {
      "version": "2"
      "repository": "@acme-inc/all-design-data-v2",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": []
    }
    module.exports = {
      version: '2'
      repository: '@workspace/repository',
      personalAccessToken: '<your-personal-access-token>',
      rules: [],
    };
    {
      "version": "2"
      "repository": "@workspace/repository",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": []
    }
    .specifyrc.js
    {
      version: '2',
      repository: '@organization/repository',
      personalAccessToken: '<your-personal-access-token>',
      rules: [
        {
          name: 'css',
          parsers: [
            {
              name: 'filter',
              options: {
                query: {
                  where: {
                    collection: '^Colors$',
                    select: {
                      children: true
                    }
                  }
                }
              }
            },
            {
              name: 'convert-color',
              options: {
                toFormat: 'hsl',
                applyTo: {
                  collection: true
                }
              }
            },
            {
              name: 'change-case',
              options: {
                change: 'names',
                toCase: 'kebabCase',
                applyTo: {
                  collection: true
                }
              }
            },
            {
              name: 'change-case',
              options: {
                change: 'modes',
                toCase: 'kebabCase',
                applyTo: {
                  collection: true
                }
              }
            },
            {
              name: 'to-css-custom-properties',
              output: {
                type: 'file',
                filePath: 'tokens.css'
              },
            },
          ],
        },
      ],
    };
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "css",
          "parsers": [
            {
              "name": "filter",
              "options": {
                "query": {
                  "where": {
                    "collection": "Colors",
                    "select": {
                      "parents": true,
                      "children": true
                    }
                  }
                }
              }
            },
            {
              "name": "convert-color",
              "options": {
                "toFormat": "hsl",
                "applyTo": {
                  "collection": true
                }
              }
            },
            {
              "name": "change-case",
              "options": {
                "change": "names",
                "toCase": "kebabCase",
                "applyTo": {
                  "collection": true
                }
              }
            },
            {
              "name": "change-case",
              "options": {
                "change": "modes",
                "toCase": "kebabCase",
                "applyTo": {
                  "collection": true
                }
              }
            },
            {
              "name": "to-css-custom-properties",
              "output": {
                "type": "file",
                "filePath": "tokens.css"
              }
            }
          ]
        }
      ]
    }
    .specifyrc.js
    module.exports = {
      version: '2',
      repository: '@acme-inc/design-system',
      personalAccessToken: '<your-personal-access-token>',
      rules: [
        {
          name: 'Generate colors as CSS Custom Properties',
          parsers: [
            {
              name: 'to-css-custom-properties',
              output: {
                type: 'file',
                filePath: 'colors.css'
              },
            },
          ],
        },
      ],
    };
    .specifyrc.json
    {
      "version": "2",
      "repository": "@acme-inc/color-themes",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Generate colors as CSS Custom Properties",
          "parsers": [
            {
              "name": "to-css-custom-properties",
              "output": {
                "type": "file",
                "filePath": "colors.css"
              }
            }
          ]
        }
      ]
    }
    .specifyrc.js
    module.exports = {
      version: '2',
      repository: '@acme-inc/design-system',
      personalAccessToken: '<your-personal-access-token>',
      rules: [
        {
          name: 'Generate colors as CSS Custom Properties',
          parsers: [
            {
              name: 'convert-color',
              options: {
                toFormat: 'rgb',
                applyTo: {
                  collection: true
                }
              }
            },
            {
              name: 'to-css-custom-properties',
              output: {
                type: 'file',
                filePath: 'colors.css'
              },
              options: {
                tokenNameTemplate: '--{{groups}}-{{token}}',
                selectorTemplate: '[data-theme=\'{{mode}}\']',
                includeCoreTokensInScopes: true
              },
            },
          ],
        },
      ],
    };
    .specifyrc.json
    
    {
      "version": "2",
      "repository": "@acme-inc/design-system",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Generate colors as CSS Custom Properties",
          "parsers": [
            {
              "name": "convert-color",
              "options": {
                "toFormat": "rgb",
                "applyTo": {
                  "collection": true
                }
              }
            },
            {
              "name": "to-css-custom-properties",
              "output": {
                "type": "file",
                "filePath": "colors.css"
              },
              "options": {
                "tokenNameTemplate": "--{{groups}}-{{token}}",
                "selectorTemplate": "[data-theme=\"{{mode}}\"]",
                "includeCoreTokensInScopes": true
              }
            }
          ]
        }
      ]
    }
    {
      "colors": {
        "$collection": { "$modes": ["Light", "Dark"] },
        "core": {
          "blue-100": {
            "$type": "color",
            "$description": "token 1 aliased with n modes within collection within n groups",
            "$value": {
              "Light": {
                "red": 219,
                "blue": 254,
                "alpha": 1,
                "green": 236,
                "model": "rgb"
              },
              "Dark": {
                "red": 41,
                "blue": 67,
                "alpha": 1,
                "green": 52,
                "model": "rgb"
              }
            }
          },
          "blue-700": {
            "$type": "color",
            "$description": "token 2 aliased with n modes within collection within n groups",
            "$value": {
              "Light": {
                "red": 17,
                "blue": 249,
                "alpha": 1,
                "green": 125,
                "model": "rgb"
              },
              "Dark": {
                "red": 96,
                "blue": 250,
                "alpha": 1,
                "green": 168,
                "model": "rgb"
              }
            }
          }
        },
        "semantic": {
          "background": {
            "button": {
              "primary": {
                "hover": {
                  "$type": "color",
                  "$description": "alias token with n modes within collection within n groups",
                  "$value": {
                    "Dark": {
                      "$mode": "dark",
                      "$alias": "colors.core.blue-100"
                    },
                    "Light": {
                      "$mode": "light",
                      "$alias": "colors.core.blue-700"
                    }
                  }
                }
              }
            }
          }
        }  
      }
    }
    colors.css
    [data-theme="dark"] {
      --core-blue-100: rgb(41, 52, 67);
      --core-blue-700: rgb(96, 168, 250);
      --semantic-background-button-primary-hover: var(--core-blue-100);
    }
    [data-theme="light"] {
      --core-blue-100: rgb(219, 236, 254);
      --core-blue-700: rgb(17, 125, 249);
      --semantic-background-button-primary-hover: var(--core-blue-700);
    }
    {
      "colors": {
        "$collection": {
          "$modes": [
            "Light",
            "Dark"
          ]
        },
        "core": {
          "blue-100": {
            "$type": "color",
            "$description": "token 1 aliased with n modes within collection within n groups",
            "$value": {
              "Light": {
                "red": 219,
                "blue": 254,
                "alpha": 1,
                "green": 236,
                "model": "rgb"
              }
            }
          }
        }
      }
    }
    Parameter
    Required
    Type
    Default
    Description

    removeSingleMode

    false

    boolean

    false

    Return a single token value if the token has only one mode.

    removeModesIfSameValue

    hashtag
    Basic usage

    to-css-custom-propertiesarrow-up-right
    {
      "colors": {
        "$collection": {
          "$modes": [
            "light",
            "dark"
          ]
        },
        "aliases": {
          "border": {
            "active": {
              "$type": "color",
              "$value": {
                "dark": {
                  "$alias": "colors.core.label.blue-base",
                  "$mode": "dark"
                },
                "light": {
                  "$alias": "colors.core.label.blue-base",
                  "$mode": "light"
                }
              }
            }
          }
        },
        "core": {
          "label": {
            "blue-base": {
              "$type": "color",
              "$value": {
                "dark": {
                  "model": "rgb",
                  "red": 96,
                  "green": 168,
                  "blue": 250,
                  "alpha": 1
                },
                "light": {
                  "model": "rgb",
                  "red": 17,
                  "green": 125,
                  "blue": 249,
                  "alpha": 1
                }
              }
            },
            "blue-lighter": {
              "$type": "color",
              "$value": {
                "dark": {
                  "model": "rgb",
                  "red": 41,
                  "green": 52,
                  "blue": 67,
                  "alpha": 1
                },
                "light": {
                  "model": "rgb",
                  "red": 219,
                  "green": 236,
                  "blue": 254,
                  "alpha": 1
                }
              }
            }
          }
        }
      },
      "dimensions": {
        "$collection": {
          "$modes": [
            "desktop",
            "mobile"
          ]
        },
        "base": {
          "dimension-01": {
            "$type": "dimension",
            "$value": {
              "mobile": {
                "value": 2,
                "unit": "px"
              },
              "desktop": {
                "value": 4,
                "unit": "px"
              }
            }
          },
          "dimension-02": {
            "$type": "dimension",
            "$value": {
              "mobile": {
                "value": 4,
                "unit": "px"
              },
              "desktop": {
                "value": 8,
                "unit": "px"
              }
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      // Only use the personalAccessToken when working with the CLI
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Generate tokens as a Tailwind theme",
          "parsers": [
            {
              "name": "to-tailwind",
              "output": {
                "type": "file",
                "filePath": "theme.js"
              }
            }
          ]
        }
      ]
    }
    theme.js
    /** @type {import('tailwindcss').Config} */
    module.exports = {
      theme: {
        extend: {
          colors: {
            colors: {
              core: {
                label: {
                  'blue-lighter': {
                    'dark': 'rgb(41, 52, 67)',
                    'light': 'rgb(219, 236, 254)'
                  },
                  'blue-base': {
                    'dark': 'rgb(96, 168, 250)',
                    'light': 'rgb(17, 125, 249)'
                  }
                }
              },
              aliases: {
                border: {
                  active: {
                    'dark': 'rgb(98, 77, 227)',
                    'light': 'rgb(235, 33, 33)'
                  }
                }
              }
            }
          },
          spacing: {
            'dimensions-base-dimension-01-desktop': '4px',
            'dimensions-base-dimension-01-mobile': '2px',
            'dimensions-base-dimension-02-desktop': '8px',
            'dimensions-base-dimension-02-mobile': '4px'
          }
        }
      }
    }
    interface parser {
      name: 'to-tailwind';
      options: {
        removeSingleMode: boolean;
        removeModesIfSameValue: boolean;
      };
      output: {
        type: 'file';
        filePath: string; // e.g theme.js
      };
    }

    false

    boolean

    false

    Return a single token value if the values of all modes of one token are identical.

    Querying a SDTF graph

    Learn more about how to query your SDTF token graph.

    hashtag
    Introduction

    Your token system can be more complex than it seems. You will often need to interact with your token graph to transform a specific set of design tokens within a Specify configuration.

    This article will help you understand how you can query your token graph to select a specific set of tokens.

    hashtag
    Compatible parsers

    hashtag
    Query structure

    Every Query holds a single a where property being an object, to select one branch of the graph, or an array of objects, to select many branches of the graph (OR statement).

    The where property splits in 3 kinds: token, group, collection - offering a dedicated set of options to match against the given kind.

    The name property accepts a RegExp for a value. These resources will help you debug your regular expressions:

    hashtag
    Where Token

    hashtag
    Where Group

    hashtag
    Where Collection

    hashtag
    Use cases

    hashtag
    Select tokens from a specific collection

    hashtag
    Select tokens from several collections matching a naming pattern

    hashtag
    Select tokens from a specific group

    hashtag
    Select tokens of a specific type from a collection

    hashtag
    Select all tokens from a collection and from groups matching a naming pattern

    hashtag
    Select tokens from several groups with different names

    hashtag
    Select design tokens from a specific type

    filter
    change-case
    convert-color
    https://regex101.com/arrow-up-right
    https://regexr.com/arrow-up-right
    Type Query = { where: Where | Array<Where> }
    type WhereToken = {
      token: 
        | string
        | {
          name?: string;
          description?: string?;
        }
      select: 
        | true
        | {
          token?: boolean;
          parents?:
            | true
            | {
              groups?: true;
              collections?: true;
            }
        }
    }
    type WhereGroup = {
      group: string;
      select: 
        | true
        | {
          group?: boolean;
          parents?:
            | true
            | {
              groups?: true;
              collections?: true;
            }
          children:
            | true
            | {
              groups?: true;
              collections?: true;
              tokens?: true;
            }
        }
    }
    type WhereCollection = {
      collection: string;
      select: 
        | true
        | {
          collection?: boolean;
          parents?:
            | true
            | {
              groups?: true;
            }
          children:
            | true
            | {
              groups?: true;
              tokens?: true;
            }
        }
    }
    {
      "primitive": {
        "$description": "`primitive` is a group used for semantic grouping.",
        "spacing": {
          "1": {
            "$type": "dimension",
            "$value": {
              "default": {
                "value": 4,
                "unit": "px"
              }
            }
          }
        }
      },
      "Colors": {
        "$description": "`Colors` is a collection.",
        "$collection": {
          "$modes": [
            "default"
          ]
        },
        "black": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#000000",
              "alpha": 1
            }
          }
        },
        "white": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#FFFFFF",
              "alpha": 1
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Get tokens from collection named 'Colors'",
          "parsers": [
            {
              "name": "filter",
              "options": {
                "query": {
                  "where": {
                    "collection": "^Colors$",
                    "select": {
                      "parents": true,
                      "children": true
                    }
                  }
                }
              }
            }
          ]
        }
      ]
    }
    {
      "Colors": {
        "$collection": {
          "$modes": [
            "default"
          ]
        },
        "black": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#000000",
              "alpha": 1
            }
          }
        },
        "white": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#FFFFFF",
              "alpha": 1
            }
          }
        }
      }
    }
    {
      "primitive": {
        "$description": "`primitive` is a group used for semantic grouping.",
        "spacing": {
          "1": {
            "$type": "dimension",
            "$description": "`primitive.spacing.1` is a dimension token without modes.",
            "$value": {
              "default": {
                "value": 4,
                "unit": "px"
              }
            }
          }
        }
      },
      "Core - Colors": {
        "$collection": {
          "$modes": [
            "default"
          ]
        },
        "black": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#000000",
              "alpha": 1
            }
          }
        },
        "white": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#FFFFFF",
              "alpha": 1
            }
          }
        }
      },
      "Core - Spacing": {
        "$collection": {
          "$modes": [
            "default"
          ]
        },
        "space-1": {
          "$type": "spacing",
          "$value": {
            "default": { "value": 1, "unit": "px" }
          }
        },
        "space-2": {
          "$type": "spacing",
          "$value": {
            "default": { "value": 2, "unit": "px" }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Get tokens from all collections whose names contain 'Core'",
          "parsers": [
            {
              "name": "filter",
              "options": {
                "where": {
                  "collection": "Core",
                  "select": {
                    "collection": true,
                    "children": {
                      "tokens": true
                    }
                  }
                }
              }
            }
          ]
        }
      ]
    }
    {
      "Core - Colors": {
        "$collection": {
          "$modes": [
            "default"
          ]
        },
        "black": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#000000",
              "alpha": 1
            }
          }
        },
        "white": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#FFFFFF",
              "alpha": 1
            }
          }
        }
      },
      "Core - Spacing": {
        "$collection": {
          "$modes": [
            "default"
          ]
        },
        "space-1": {
          "$type": "spacing",
          "$value": {
            "default": {
              "value": 1,
              "unit": "px"
            }
          }
        },
        "space-2": {
          "$type": "spacing",
          "$value": {
            "default": {
              "value": 2,
              "unit": "px"
            }
          }
        }
      }
    }
    {
      "primitive": {
        "$description": "`primitive` is a group used for semantic grouping.",
        "spacing": {
          "1": {
            "$type": "dimension",
            "$description": "`primitive.spacing.1` is a dimension token without modes.",
            "$value": {
              "default": {
                "value": 4,
                "unit": "px"
              }
            }
          }
        },
        "font": {
          "interBold": {
            "$type": "font",
            "$value": {
              "default": {
                "family": "Inter",
                "postScriptName": "Inter Bold",
                "weight": "bold",
                "style": "normal",
                "files": [
                  {
                    "format": "ttf",
                    "url": "https://static.specifyapp.com/sdtf-seeds/inter-bold.ttf",
                    "provider": "Specify"
                  }
                ]
              }
            }
          }
        }
      },
      "Text Styles": {
        "heading": {
          "$type": "textStyle",
          "$value": {
            "default": {
              "font": {
                "$alias": "primitive.font.interBold",
                "$mode": "default"
              },
              "color": null,
              "fontSize": {
                "value": 32,
                "unit": "px"
              },
              "lineHeight": {
                "value": 40,
                "unit": "px"
              },
              "fontFeatures": null,
              "letterSpacing": null,
              "paragraphSpacing": null,
              "textAlignHorizontal": null,
              "textAlignVertical": null,
              "textDecoration": null,
              "textIndent": null,
              "textTransform": null
            }
          }
        },
        "display": {
          "$type": "textStyle",
          "$value": {
            "default": {
              "font": {
                "$alias": "primitive.font.interBold",
                "$mode": "default"
              },
              "color": null,
              "fontSize": {
                "value": 56,
                "unit": "px"
              },
              "lineHeight": {
                "value": 64,
                "unit": "px"
              },
              "fontFeatures": null,
              "letterSpacing": {
                "value": -1,
                "unit": "px"
              },
              "paragraphSpacing": null,
              "textAlignHorizontal": null,
              "textAlignVertical": null,
              "textDecoration": null,
              "textIndent": null,
              "textTransform": null
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Get tokens from the group named 'Text Styles'",
          "parsers": [
            {
              "name": "filter",
              "options": {
                "query": {
                  "where": {
                    "group": "^Text Styles$",
                    "select": {
                      "parents": true,
                      "children": true
                    }
                  }
                }
              }
            }
          ]
        }
      ]
    }
    {
      "Text Styles": {
        "display": {
          "$type": "textStyle",
          "$value": {
            "default": {
              "fontSize": {
                "value": 56,
                "unit": "px"
              },
              "color": null,
              "fontFeatures": null,
              "lineHeight": {
                "value": 64,
                "unit": "px"
              },
              "letterSpacing": {
                "value": -1,
                "unit": "px"
              },
              "paragraphSpacing": null,
              "textAlignHorizontal": null,
              "textAlignVertical": null,
              "textDecoration": null,
              "textIndent": null,
              "textTransform": null,
              "font": {
                "$alias": "primitive.font.interBold",
                "$mode": "default"
              }
            }
          }
        },
        "heading": {
          "$type": "textStyle",
          "$value": {
            "default": {
              "fontSize": {
                "value": 32,
                "unit": "px"
              },
              "color": null,
              "fontFeatures": null,
              "lineHeight": {
                "value": 40,
                "unit": "px"
              },
              "letterSpacing": null,
              "paragraphSpacing": null,
              "textAlignHorizontal": null,
              "textAlignVertical": null,
              "textDecoration": null,
              "textIndent": null,
              "textTransform": null,
              "font": {
                "$alias": "primitive.font.interBold",
                "$mode": "default"
              }
            }
          }
        }
      }
    }
    {
      "Android": {
        "$collection": {
          "$modes": [
            "default"
          ]
        },
        "black": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#000000",
              "alpha": 1
            }
          }
        },
        "white": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#FFFFFF",
              "alpha": 1
            }
          }
        }
      },
      "primitive": {
        "gray": {
          "0": {
            "$type": "color",
            "$value": {
              "default": {
                "model": "hex",
                "hex": "#f8f9fa",
                "alpha": 1
              }
            }
          },
          "9": {
            "$type": "color",
            "$value": {
              "default": {
                "model": "hex",
                "hex": "#212529",
                "alpha": 1
              }
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Get color tokens from the 'Android' collection",
          "parsers": [
            {
              "name": "filter",
              "options": {
                "query": {
                  "where": {
                    "collection": "^Android$",
                    "andWhere": {
                      "token": ".*",
                      "withTypes": { "include": ["color"] },
                      "select": {
                        "token": true,
                        "parents": {
                          "collections": true
                        }
                      }
                    }
                  }            
                }
              }
            }
          ]
        }
      ]
    }
    {
      "Android": {
        "$collection": {
          "$modes": [
            "default"
          ]
        },
        "black": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#000000",
              "alpha": 1
            }
          }
        },
        "white": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#FFFFFF",
              "alpha": 1
            }
          }
        }
      }
    }
    {
      "Android": {
        "$collection": {
          "$modes": [
            "default"
          ]
        },
        "primitive": {
          "gray": {
            "$type": "color",
            "$value": {
              "default": {
                "model": "hex",
                "hex": "#f8f9fa",
                "alpha": 1
              }
            }
          }
        },
        "components": {
          "black": {
            "$type": "color",
            "$value": {
              "default": {
                "model": "hex",
                "hex": "#000000",
                "alpha": 1
              }
            }
          },
          "white": {
            "$type": "color",
            "$value": {
              "default": {
                "model": "hex",
                "hex": "#FFFFFF",
                "alpha": 1
              }
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Get color tokens from the 'Android' collection and from all groups named 'components'",
          "parsers": [
            {
              "name": "filter",
              "options": {
                "query": {
                  "where": {
                    "collection": "^Android$",
                    "andWhere": {
                      "group": "^components$",
                      "andWhere": {
                        "token": ".*",
                        "withTypes": {
                          "include": ["color"]
                        },
                        "select": {
                          "token": true,
                          "parents": true
                        }
                      }
                    }
                  }
                }
              }
            }
          ]
        }
      ]
    }
    {
      "Android": {
        "$collection": {
          "$modes": [
            "default"
          ]
        },
        "components": {
          "black": {
            "$type": "color",
            "$value": {
              "default": {
                "model": "hex",
                "hex": "#000000",
                "alpha": 1
              }
            }
          },
          "white": {
            "$type": "color",
            "$value": {
              "default": {
                "model": "hex",
                "hex": "#FFFFFF",
                "alpha": 1
              }
            }
          }
        }
      }
    }
    {
      "group1": {
        "token1": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#000000",
              "alpha": 1
            }
          }
        }
      },
      "group2": {
        "token2": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#FFFFFF",
              "alpha": 1
            }
          }
        }
      },
      "group3": {
        "token3": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#FFFFFF",
              "alpha": 1
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Get tokens from the group named 'Components' and/or from the group named 'Semantic'",
          "parsers": [
            {
              "name": "filter",
              "options": {
                "query": {
                  "where": {
                    "group": "^group1$|^group2$",
                    "andWhere": {
                      "token": ".*",
                      "select": {
                        "token": true,
                        "parents": true
                      }
                    }
                  }
                }
              }
            }
          ]
        }
      ]
    }
    {
      "group1": {
        "token1": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#000000",
              "alpha": 1
            }
          }
        }
      },
      "group2": {
        "token2": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#FFFFFF",
              "alpha": 1
            }
          }
        }
      }
    }
    {
      "dimensions": {
        "spacing": {
          "1": {
            "$type": "dimension",
            "$value": {
              "default": {
                "value": 4,
                "unit": "px"
              }
            }
          }
        }
      },
      "colors": {
        "black": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#000000",
              "alpha": 1
            }
          }
        }
      }
    }
    .specifyrc.json
    {
      "version": "2",
      "repository": "@organization/repository",
      "personalAccessToken": "<your-personal-access-token>",
      "rules": [
        {
          "name": "Get color tokens from the 'Android' collection and from all groups named 'components'",
          "parsers": [
            {
              "name": "filter",
              "options": {
                "query": {
                  "where": {
                    "token": ".*",
                    "withTypes": {
                      "include": [
                        "color"
                      ]
                    },
                    "select": {
                      "parents": true
                    }
                  }
                }
              }
            }
          ]
        }
      ]
    }
    {
      "colors": {
        "black": {
          "$type": "color",
          "$value": {
            "default": {
              "model": "hex",
              "hex": "#000000",
              "alpha": 1
            }
          }
        }
      }
    }
    convert-dimension