{"id":13394171,"url":"https://github.com/salesforce-ux/theo","last_synced_at":"2025-05-14T02:05:30.408Z","repository":{"id":14284277,"uuid":"16992387","full_name":"salesforce-ux/theo","owner":"salesforce-ux","description":"Theo is a an abstraction for transforming and formatting Design Tokens","archived":false,"fork":false,"pushed_at":"2021-10-12T08:27:17.000Z","size":2315,"stargazers_count":1983,"open_issues_count":30,"forks_count":120,"subscribers_count":44,"default_branch":"master","last_synced_at":"2025-05-05T18:38:02.110Z","etag":null,"topics":["design-systems","design-tokens"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/salesforce-ux.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null}},"created_at":"2014-02-19T17:25:34.000Z","updated_at":"2025-04-18T07:18:11.000Z","dependencies_parsed_at":"2022-07-15T21:16:56.540Z","dependency_job_id":null,"html_url":"https://github.com/salesforce-ux/theo","commit_stats":null,"previous_names":[],"tags_count":49,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salesforce-ux%2Ftheo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salesforce-ux%2Ftheo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salesforce-ux%2Ftheo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salesforce-ux%2Ftheo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/salesforce-ux","download_url":"https://codeload.github.com/salesforce-ux/theo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253418941,"owners_count":21905334,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["design-systems","design-tokens"],"created_at":"2024-07-30T17:01:11.305Z","updated_at":"2025-05-14T02:05:25.393Z","avatar_url":"https://github.com/salesforce-ux.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","Tools","Design tokens","📦 Legacy \u0026 Inactive Projects"],"sub_categories":["Collections","Tools","Misc"],"readme":"# \u003cimg src=\"https://raw.githubusercontent.com/salesforce-ux/theo/master/assets/theo.png\" alt=\"Theo logo\" width=\"28\" /\u003e Theo\n\n[![Build Status][travis-image]][travis-url]\n[![NPM version][npm-image]][npm-url]\n\nTheo is an abstraction for transforming and formatting [Design Tokens](#overview).\n\n\u003e ### Looking for the gulp plugin?\n\u003e\n\u003e As of Theo v6, the gulp plugin is distributed as a separate package: [gulp-theo](https://www.npmjs.com/package/gulp-theo).\n\n## Example\n\n```yaml\n# buttons.yml\nprops:\n  button_background:\n    value: \"{!primary_color}\"\nimports:\n  - ./aliases.yml\nglobal:\n  type: color\n  category: buttons\n```\n\n```yaml\n# aliases.yml\naliases:\n  primary_color:\n    value: \"#0070d2\"\n```\n\n```js\nconst theo = require(\"theo\");\n\ntheo\n  .convert({\n    transform: {\n      type: \"web\",\n      file: \"buttons.yml\"\n    },\n    format: {\n      type: \"scss\"\n    }\n  })\n  .then(scss =\u003e {\n    // $button-background: rgb(0, 112, 210);\n  })\n  .catch(error =\u003e console.log(`Something went wrong: ${error}`));\n```\n\n## Transforms\n\nTheo is divided into two primary features: transforms and formats.\n\nTransforms are a named group of value transforms. Theo ships with several predefined transforms.\n\n| Name      | Value Transforms                                                |\n| --------- | --------------------------------------------------------------- |\n| `raw`     | `[]`                                                            |\n| `web`     | `['color/rgb']`                                                 |\n| `ios`     | `['color/rgb', 'relative/pixelValue', 'percentage/float']`      |\n| `android` | `['color/hex8argb', 'relative/pixelValue', 'percentage/float']` |\n\n### Value Transforms\n\nValue transforms are used to conditionaly transform the value of a property. Below are the value transforms that ship with Theo along with the predicate that triggers them.\n\n| Name                  | Predicate               | Description                                                       |\n| --------------------- | ----------------------- | ----------------------------------------------------------------- |\n| `color/rgb`           | `prop.type === 'color'` | Convert to rgb                                                    |\n| `color/hex`           | `prop.type === 'color'` | Convert to hex                                                    |\n| `color/hex8rgba`      | `prop.type === 'color'` | Convert to hex8rgba                                               |\n| `color/hex8argb`      | `prop.type === 'color'` | Convert to hex8argb                                               |\n| `percentage/float`    | `/%/.test(prop.value)`  | Convert a percentage to a decimal percentage                      |\n| `relative/pixel`      | `isRelativeSpacing`     | Convert a r/em value to a pixel value                             |\n| `relative/pixelValue` | `isRelativeSpacing`     | Convert a r/em value to a pixel value (excluding the `px` suffix) |\n\n### Custom Transforms / Value Transforms\n\n```javascript\n/*\n{\n  CUSTOM_EASING: {\n    type: 'easing',\n    value: [1,2,3,4]\n  }\n}\n*/\n\ntheo.registerValueTransform(\n  // Name to be used with registerTransform()\n  \"easing/web\",\n  // Determine if the value transform\n  // should be run on the specified prop\n  prop =\u003e prop.get(\"type\") === \"easing\",\n  // Return the new value\n  prop =\u003e {\n    const [x1, y1, x2, y2] = prop.get(\"value\").toArray();\n    return `cubic-bezier(${x1}, ${y1}, ${x2}, ${y2})`;\n  }\n);\n\n// Override the default \"web\" transform\ntheo.registerTransform(\"web\", [\"color/rgb\", \"easing/web\"]);\n```\n\n## Formats\n\nTheo ships with the following predefined formats.\n\n### custom-properties.css\n\n```css\n:root {\n  /* If prop has 'comment' key, that value will go here. */\n  --prop-name: PROP_VALUE;\n}\n```\n\n### cssmodules.css\n\n```css\n/* If prop has 'comment' key, that value will go here. */\n@value prop-name: PROP_VALUE;\n```\n\n### scss\n\n```sass\n// If prop has 'comment' key, that value will go here.\n$prop-name: PROP_VALUE;\n```\n\n### sass\n\n```sass\n// If prop has 'comment' key, that value will go here.\n$prop-name: PROP_VALUE\n```\n\n### less\n\n```less\n// If prop has 'comment' key, that value will go here.\n@prop-name: PROP_VALUE;\n```\n\n### styl\n\n```styl\n// If prop has 'comment' key, that value will go here.\n$prop-name = PROP_VALUE\n```\n\n### map.scss\n\n```sass\n$file-name-map: (\n  // If prop has 'comment' key, that value will go here.\n  \"prop-name\": (PROP_VALUE),\n);\n```\n\n### map.variables.scss\n\n```sass\n$file-name-map: (\n  // If prop has 'comment' key, that value will go here.\n  \"prop-name\": ($prop-name)\n);\n```\n\n### list.scss\n\n```sass\n$file-name-list: (\n  // If prop has 'comment' key, that value will go here.\n  \"prop-name\"\n);\n```\n\n### module.js\n\n```js\n// If prop has 'comment' key, that value will go here.\nexport const propName = \"PROP_VALUE\";\n```\n\n### common.js\n\n```js\nmodule.exports = {\n  // If prop has 'comment' key, that value will go here.\n  propName: \"PROP_VALUE\"\n};\n```\n\n### html\n\n\u003cimg src=\"https://raw.githubusercontent.com/salesforce-ux/theo/master/assets/doc_example.png\" alt=\"\" height=\"400\" /\u003e\n\n```js\n// When passing \"format\" options to theo.convert(), this format can be\n// passed with an additional options object.\nlet formatOptions = {\n  type: \"html\",\n  options: {\n    transformPropName: name =\u003e name.toUpperCase()\n  }\n};\n```\n\n#### Configurable options\n\n| Option              | Type       | Default                                                  | Description                    |\n| ------------------- | ---------- | -------------------------------------------------------- | ------------------------------ |\n| `transformPropName` | `function` | [`lodash/camelCase`](https://lodash.com/docs/#camelCase) | Converts `name` to camel case. |\n\n#### Supported categories\n\nTokens are grouped by category then categories are conditionally rendered under a human-friendly display name. Tokens with `category` values not in this list will still be converted and included in the generated output for all other formats.\n\n| Category              | Friendly Name          |\n| --------------------- | ---------------------- |\n| `spacing`             | Spacing                |\n| `sizing`              | Sizing                 |\n| `font`                | Fonts                  |\n| `font-style`          | Font Styles            |\n| `font-weight`         | Font Weights           |\n| `font-size`           | Font Sizes             |\n| `line-height`         | Line Heights           |\n| `font-family`         | Font Families          |\n| `border-style`        | Border Styles          |\n| `border-color`        | Border Colors          |\n| `radius`              | Radius                 |\n| `border-radius`       | Border Radii           |\n| `hr-color`            | Horizontal Rule Colors |\n| `background-color`    | Background Colors      |\n| `gradient`            | Gradients              |\n| `background-gradient` | Background Gradients   |\n| `drop-shadow`         | Drop Shadows           |\n| `box-shadow`          | Box Shadows            |\n| `inner-shadow`        | Inner Drop Shadows     |\n| `text-color`          | Text Colors            |\n| `text-shadow`         | Text Shadows           |\n| `time`                | Time                   |\n| `media-query`         | Media Queries          |\n\n### json\n\n```json\n{\n  \"PROP_NAME\": \"PROP_VALUE\"\n}\n```\n\n### raw.json\n\n```json5\n{\n  props: {\n    PROP_NAME: {\n      value: \"PROP_VALUE\",\n      type: \"PROP_TYPE\",\n      category: \"PROP_CATEGORY\"\n    }\n  }\n}\n```\n\n### ios.json\n\n```json5\n{\n  properties: [\n    {\n      name: \"propName\",\n      value: \"PROP_VALUE\",\n      type: \"PROP_TYPE\",\n      category: \"PROP_CATEGORY\"\n    }\n  ]\n}\n```\n\n### android.xml\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"utf-8\"?\u003e\n\u003cresources\u003e\n  \u003ccolor name=\"PROP_NAME\" category=\"PROP_CATEGORY\"\u003ePROP_VALUE\u003c/color\u003e\n  \u003cdimen name=\"PROP_NAME\" category=\"PROP_CATEGORY\"\u003ePROP_VALUE\u003c/dimen\u003e\n  \u003cstring name=\"PROP_NAME\" category=\"PROP_CATEGORY\"\u003ePROP_VALUE\u003c/string\u003e\n  \u003cinteger name=\"PROP_NAME\" category=\"PROP_CATEGORY\"\u003ePROP_VALUE\u003c/integer\u003e\n  \u003cproperty name=\"PROP_NAME\" category=\"PROP_CATEGORY\"\u003ePROP_VALUE\u003c/property\u003e\n\u003c/resources\u003e\n```\n\n### aura.tokens\n\n```xml\n\u003caura:tokens\u003e\n  \u003caura:token name=\"propName\" value=\"PROP_VALUE\" /\u003e\n\u003c/aura:tokens\u003e\n```\n\n### Custom Format (Handlebars)\n\n```js\nconst theo = require(\"theo\");\n\ntheo.registerFormat(\n  \"array.js\",\n  `\n  // Source: {{stem meta.file}}\n  module.exports = [\n    {{#each props as |prop|}}\n      {{#if prop.comment}}{{{commoncomment prop.comment}}}{{/if}}\n      ['{{camelcase prop.name}}', '{{prop.value}}'],\n    {{/each}}\n  ]\n`\n);\n```\n\nA plethora of [handlebars helpers](https://github.com/helpers/handlebars-helpers#helpers),\nsuch as `camelcase` and `stem`, are available and will assist in formatting strings in templates.\n\n### Custom Format (function)\n\nYou may also register a format using a function:\n\n```js\nconst camelCase = require(\"lodash/camelCase\");\nconst path = require(\"path\");\nconst theo = require(\"theo\");\n\ntheo.registerFormat(\"array.js\", result =\u003e {\n  // \"result\" is an Immutable.Map\n  // https://facebook.github.io/immutable-js/\n  return `\n    module.exports = [\n      // Source: ${path.basename(result.getIn([\"meta\", \"file\"]))}\n      ${result\n        .get(\"props\")\n        .map(\n          prop =\u003e `\n        ['${camelCase(prop.get(\"name\"))}', '${prop.get(\"value\")}'],\n      `\n        )\n        .toJS()}\n    ]\n  `;\n});\n```\n\n## API\n\n```js\ntype ConvertOptions = {\n  transform: TransformOptions,\n  format: FormatOptions,\n  /*\n    This option configures theo to resolve aliases. It is set (true) by default and\n    currently CANNOT be disabled.\n  */\n  resolveAliases?: boolean,\n\n  // This option configures theo to resolve aliases in metadata. This is off (false) by default.\n  resolveMetaAliases?: boolean\n}\n\ntype TransformOptions = {\n  // If no \"type\" is specified, values will not be transformed\n  type?: string,\n  // Path to a token file\n  // or just a filename if using the \"data\" option\n  file: string,\n  // Pass in a data string instead of reading from a file\n  data?: string\n}\n\ntype FormatOptions = {\n  type: string,\n  // Available to the format function/template\n  options?: object\n}\n\ntype Prop = Immutable.Map\ntype Result = Immutable.Map\n\ntheo.convert(options: ConvertOptions): Promise\u003cstring\u003e\n\ntheo.convertSync(options: ConvertOptions): string\n\ntheo.registerFormat(\n  name: string,\n  // Either a handlebars template string\n  // or a function that returns a string\n  format: string | (result: Result) =\u003e string\n): void\n\ntheo.registerValueTransform(\n  // Referenced in \"registerTransform\"\n  name: string,\n  // Indicate if the transform should run for the provided prop\n  predicate: (prop: Prop) =\u003e boolean,\n  // Return the new \"value\"\n  transform: (prop: Prop) =\u003e any\n): void\n\ntheo.registerTransform(\n  name: string,\n  // An array of registered value transforms\n  valueTransforms: Array\u003cstring\u003e\n): void\n```\n\n## CLI\n\nPlease refer to the [documentation of the CLI](https://github.com/salesforce-ux/theo/blob/master/CLI.md)\n\n## Design Tokens \u003ca name=\"overview\"\u003e\u003c/a\u003e\n\nTheo consumes **Design Token** files which are a central location to store\ndesign related information such as colors, fonts, widths, animations, etc. These raw\nvalues can then be transformed and formatted to meet the needs of any platform.\n\nLet's say you have a web, native iOS, and native Android application that\nwould like to share information such as background colors.\n\nThe web might like to consume the colors as **hsla** values\nformatted as Sass variables in an **.scss** file.\n\niOS might like **rgba** values formatted as **.json**.\n\nFinally, Android might like **8 Digit Hex (AARRGGBB)** values formatted as **.xml**.\n\nInstead of hard coding this information in each platform/format, Theo\ncan consume the centralized **Design Tokens** and output files for\neach platform.\n\n### Spec\n\nA _Design Token_ file is written in either\n[JSON](http://json.org/) ([JSON5](http://json5.org/) supported)\nor [YAML](http://yaml.org/) and should conform to the following spec:\n\n```json5\n{\n  // Required\n  // A map of property names and value objects\n  props: {\n    color_brand: {\n      // Required\n      // Can be any valid JSON value\n      value: \"#ff0000\",\n\n      // Required\n      // Describe the type of value\n      // [color|number|...]\n      type: \"color\",\n\n      // Required\n      // Describe the category of this property\n      // Often used for style guide generation\n      category: \"background\",\n\n      // Optional\n      // This value will be included during transform\n      // but excluded during formatting\n      meta: {\n        // This value might be needed for some special transform\n        foo: \"bar\"\n      }\n    }\n  },\n\n  // Optional\n  // Alternatively, you can define props as an array\n  // Useful for maintaining source order in output tokens\n  props: [\n    {\n      // Required\n      name: \"color_brand\"\n\n      // All other properties same as above\n    }\n  ],\n\n  // Optional\n  // This object will be merged into each property\n  // Values defined on a property level will take precedence\n  global: {\n    category: \"some-category\",\n    meta: {\n      foo: \"baz\"\n    }\n  },\n\n  // Optional\n  // Share values across multiple props\n  // Aliases are resolved like: {!sky}\n  aliases: {\n    sky: \"blue\",\n    grass: {\n      value: \"green\",\n      yourMetadata: \"How grass looks\"\n    }\n  },\n\n  // Optional\n  // Array of design token files to be imported\n  // \"aliases\" will be imported as well\n  // \"aliases\" will already be resolved\n  // \"global\" will already be merged into each prop\n  // Imports resolve according to the Node.js module resolution algorithm:\n  // https://nodejs.org/api/modules.html#modules_all_together\n  imports: [\n    // Absolute file path\n    \"/home/me/file.json\",\n    // Relative file path: resolves from the directory of the file where the import occurs\n    \"./some/dir/file.json\",\n    // Module path\n    \"some-node-module\"\n  ]\n}\n```\n\n[npm-url]: https://npmjs.org/package/theo\n[npm-image]: http://img.shields.io/npm/v/theo.svg\n[travis-url]: https://travis-ci.org/salesforce-ux/theo\n[travis-image]: http://img.shields.io/travis/salesforce-ux/theo.svg\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsalesforce-ux%2Ftheo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsalesforce-ux%2Ftheo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsalesforce-ux%2Ftheo/lists"}