├── .env.example ├── .gitignore ├── README.md ├── dist ├── .gitignore └── .npmignore ├── package-lock.json ├── package.json ├── scripts └── build.js └── src └── index.js /.env.example: -------------------------------------------------------------------------------- 1 | GITHUB_TOKEN="f941e0..." 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | .env 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # U.S. Tailwind Design System 2 | 3 | A [TailwindCSS](https://tailwindcss.com/) plugin for adding [U.S. Web Design System](https://designsystem.digital.gov/) design tokens to supported Tailwind utilities. For use in utility-first projects that favor a JavaScript based configuration and do not require USWDS provided components or page templates out of the box. 4 | 5 | ## Install 6 | 7 | 1. Install the plugin: 8 | 9 | ``` 10 | # Using npm 11 | npm install @hursey013/tailwindcss-uswds --save-dev 12 | 13 | # Using Yarn 14 | yarn add @hursey013/tailwindcss-uswds -D 15 | ``` 16 | 17 | 2. Add it to your tailwind.config.js file: 18 | 19 | ```js 20 | // tailwind.config.js 21 | module.exports = { 22 | // ... 23 | plugins: [require("@hursey013/tailwindcss-uswds")] 24 | }; 25 | ``` 26 | 27 | ## Options 28 | 29 | ### `fontPath` (optional) 30 | 31 | `tailwindcss-uswds` provides the necessary font files in order to generate the required USWDS `@font-face` rules. If you would prefer to copy the font files directly into your project folder you can update the path to the font directory: 32 | 33 | ```js 34 | // tailwind.config.js 35 | module.exports = { 36 | // ... 37 | plugins: [ 38 | require("@hursey013/tailwindcss-uswds")({ 39 | fontPath: "../path/to/fonts" 40 | }) 41 | ] 42 | }; 43 | ``` 44 | 45 | ### `overrides` (optional) 46 | 47 | By default, all supported utilities use the `standard` set of design tokens provided by USWDS: 48 | 49 | ```js 50 | // tailwind.config.js 51 | module.exports = { 52 | // ... 53 | plugins: [ 54 | require("@hursey013/tailwindcss-uswds")({ 55 | // ... 56 | overrides: { 57 | borderRadius: "standard", 58 | borderWidth: "standard", 59 | boxShadow: "standard", 60 | colors: "standard", 61 | cursor: "standard", 62 | flex: "standard", 63 | fontFamily: "standard", 64 | fontFeatureSettings: "standard", 65 | fontSize: "standard", 66 | fontWeight: "standard", 67 | gap: "standard", 68 | height: "standard", 69 | letterSpacing: "standard", 70 | lineHeight: "standard", 71 | margin: "standard", 72 | maxHeight: "standard", 73 | maxWidth: "standard", 74 | measure: "standard", 75 | minHeight: "standard", 76 | minWidth: "standard", 77 | opacity: "standard", 78 | order: "standard", 79 | padding: "standard", 80 | screens: "standard", 81 | textIndent: "standard", 82 | width: "standard", 83 | zIndex: "standard" 84 | } 85 | }) 86 | ] 87 | }; 88 | ``` 89 | 90 | To prevent USWDS design tokens from overriding the default Tailwind values, pass a value of `false`: 91 | 92 | ```js 93 | // tailwind.config.js 94 | module.exports = { 95 | // ... 96 | plugins: [ 97 | require("@hursey013/tailwindcss-uswds")({ 98 | // ... 99 | overrides: { 100 | // .. 101 | flex: false 102 | // ... 103 | } 104 | }) 105 | ] 106 | }; 107 | ``` 108 | 109 | To use an extended set of USWDS design tokens for a particular utility, pass a value of `extended` : 110 | 111 | ```js 112 | // tailwind.config.js 113 | module.exports = { 114 | // ... 115 | plugins: [ 116 | require("@hursey013/tailwindcss-uswds")({ 117 | // ... 118 | overrides: { 119 | // .. 120 | color: "extended" 121 | // ... 122 | } 123 | }) 124 | ] 125 | }; 126 | ``` 127 | 128 | Currently `color`, `borderRadius`, `fontSize`, `fontWeight`, `letterSpacing`, and `lineHeight` support `extended`. 129 | 130 | ## Usage 131 | 132 | ### Utilities 133 | 134 | Generated utility classes follow the default naming conventions provided by Tailwind with USWDS design tokens as values. This creates shorter class names and may also change the utility classname itself: 135 | 136 | ```css 137 | /* .border-bottom-1 becomes: */ 138 | .border-b-1 { 139 | border-bottom: 0.75rem solid; 140 | } 141 | 142 | /* .margin-bottom-neg-2px becomes: */ 143 | .-mb-2px { 144 | margin-bottom: -2px; 145 | } 146 | 147 | /* .radius-right-lg becomes: */ 148 | .rounded-r-lg { 149 | border-top-right-radius: 0.5rem; 150 | border-bottom-right-radius: 0.5rem; 151 | } 152 | ``` 153 | 154 | Out of the box, `tailwindcss-uswds` provides the following USWDS design tokens to each specific utility: 155 | 156 | | Key | Classes | Reference | 157 | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | 158 | | **`borderRadius`** | `.rounded-{t\|r\|b\|l\|tl\|tr\|br\|bl}-{0, sm, md, lg, pill}` | [TW](https://tailwindcss.com/docs/border-radius) \| [USWDS](https://designsystem.digital.gov/utilities/border/#utility-radius) | 159 | | **`borderWidth`** | `.border-{t\|r\|b\|l}-{0, 1px, 2px, 05, 1, 105, 2, 205, 3}` | [TW](https://tailwindcss.com/docs/border-width) \| [USWDS](https://designsystem.digital.gov/utilities/border/#utility-border-width) | 160 | | **`boxShadow`** | `.shadow-{none, 1, 2, 3, 4, 5}` | [TW](https://tailwindcss.com/docs/box-shadow) \| [USWDS](https://designsystem.digital.gov/utilities/shadow/) | 161 | | **`colors`** | See [colors](#colors) section | 162 | | **`cursor`** | `.cursor-{auto, default, move, not-allowed, pointer, wait}` | [TW](https://tailwindcss.com/docs/cursor) | 163 | | **`flex`** | `.flex-{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, auto, fill}` | [TW](https://tailwindcss.com/docs/flex) \| [USWDS](https://designsystem.digital.gov/utilities/flex/#utility-flex) | 164 | | **`fontFamily`** | `.font-{alt, body, code, heading, mono, sans, serif, ui}` | [TW](https://tailwindcss.com/docs/font-family) \| [USWDS](https://designsystem.digital.gov/utilities/font-size-and-family/#utility-font-family) | 165 | | **`fontSize`** | `.text-{alt, body, code, heading, mono, sans, serif, ui}-{3xs, 2xs, xs, sm, md, lg, xl, 2xl, 3xl}` | [TW](https://tailwindcss.com/docs/font-size) \| [USWDS](https://designsystem.digital.gov/utilities/font-size-and-family/#utility-font) | 166 | | **`fontWeight`** | `.font-{light, normal, bold}` | [TW](https://tailwindcss.com/docs/font-weight) \| [USWDS](https://designsystem.digital.gov/utilities/text-styles/#font-weight) | 167 | | **`gap`** | `.gap-{0, 2px, 05, 1, 2, 3, 4, 5, 6, sm, md, lg}` | [TW](https://tailwindcss.com/docs/gap) \| [USWDS](https://designsystem.digital.gov/utilities/layout-grid/#gutters) | 168 | | **`height`** | `.h-{auto, 0, 1px, 2px, 05, 1, 105, 2, 205, 3, 4, 5, 6, 7, 8, 9, 10, 15, card, card-lg, mobile, full, viewport}` | [TW](https://tailwindcss.com/docs/height) \| [USWDS](https://designsystem.digital.gov/utilities/height-and-width/#height) | 169 | | **`letterSpacing`** | `.tracking-{-3, -2, -1, auto, 1, 2, 3}` | [TW](https://tailwindcss.com/docs/letter-spacing) \| [USWDS](https://designsystem.digital.gov/utilities/text-styles/#letterspacing) | 170 | | **`lineHeight`** | `.leading-{alt, body, code, heading, mono, sans, serif, ui}-{1, 2, 3, 4, 5, 6}` | [TW](https://tailwindcss.com/docs/line-height) \| [USWDS](https://designsystem.digital.gov/utilities/paragraph-styles/#line-height) | 171 | | **`margin`** | `.m{t\|r\|b\|l\|x\|y}-{-1px, -2px, -05, -1, -105, -2, -205, -3, 0, 1px, 2px, 05, 1, 105, 2, 205, 3, 4, 5, 6, 7, 8, 9, 10, 15, auto, card, card-lg, mobile}` | [TW](https://tailwindcss.com/docs/margin) \| [USWDS](https://designsystem.digital.gov/utilities/margin-and-padding/#margin) | 172 | | **`maxHeight`** | `.max-h-{none, 05, 1, 105, 2, 205, 3, 4, 5, 6, 7, 8, 9, 10, 15, card, card-lg, mobile, mobile-lg, tablet, tablet-lg, viewport}` | [TW](https://tailwindcss.com/docs/max-height) \| [USWDS](https://designsystem.digital.gov/utilities/height-and-width/#maxh) | 173 | | **`maxWidth`** | `.max-w-{none, 05, 1, 105, 2, 205, 3, 4, 5, 6, 7, 8, 9, 10, 15, card, card-lg, mobile, mobile-lg, tablet, tablet-lg, desktop, desktop-lg, widescreen, full}` | [TW](https://tailwindcss.com/docs/max-width) \| [USWDS](https://designsystem.digital.gov/utilities/height-and-width/#maxw) | 174 | | **`minHeight`** | `.min-h-{none, 0, 05, 1, 105, 2, 205, 3, 4, 5, 6, 7, 8, 9, 10, 15, card, card-lg, mobile, mobile-lg, tablet, tablet-lg, viewport}` | [TW](https://tailwindcss.com/docs/min-height) \| [USWDS](https://designsystem.digital.gov/utilities/height-and-width/#minh) | 175 | | **`minWidth`** | `.min-w-{none, 0, 05, 1, 105, 2, 205, 3, 4, 5, 6, 7, 8, 9, 10, 15}` | [TW](https://tailwindcss.com/docs/min-width) \| [USWDS](https://designsystem.digital.gov/utilities/height-and-width/#minw) | 176 | | **`opacity`** | `.opacity-{0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}` | [TW](https://tailwindcss.com/docs/border-radius) \| [USWDS](https://designsystem.digital.gov/utilities/display/#opacity) | 177 | | **`order`** | `.order-{first, last, initial, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}` | [TW](https://tailwindcss.com/docs/opacity) \| [USWDS](https://designsystem.digital.gov/utilities/flex/#utility-order) | 178 | | **`padding`** | `.p{t\|r\|b\|l\|x\|y}-{0, 1px, 2px, 05, 1, 105, 2, 205, 3, 4, 5, 6, 7, 8, 9, 10, 15}` | [TW](https://tailwindcss.com/docs/padding) \| [USWDS](https://designsystem.digital.gov/utilities/margin-and-padding/#padding) | 179 | | **`screens`** | See [breakpoints](#breakpoints) section | 180 | | **`width`** | `.w-{auto, 0, 05, 1, 105, 2, 205, 3, 4, 5, 6, 7, 8, 9, 10, 15, card, card-lg, mobile, mobile-lg, tablet, tablet-lg, desktop, desktop-lg, widescreen, full}` | [TW](https://tailwindcss.com/docs/width) \| [USWDS](https://designsystem.digital.gov/utilities/height-and-width/#width) | 181 | | **`zIndex`** | `.z-{auto, bottom, top, 0, 100, 200, 300, 400, 500}` | [TW](https://tailwindcss.com/docs/z-index) \| [USWDS](https://designsystem.digital.gov/utilities/display/#z-index) | 182 | 183 | Unless mentioned above, all other utilities provided by Tailwind will be available with their default Tailwind values. 184 | 185 | Do note that some Tailwind utilities inherit values defined elsewhere in the config file. For example, the USWDS design tokens defined in `opacity` will also be provided to Tailwind's `borderOpacity`, `backgroundOpacity`, `placeholderOpacity`, and `textOpacity` utilities. Similar functionality is provided for `color` based utilities. 186 | 187 | ### Additional utilities 188 | 189 | Several USWDS-specific utilities are available in addition to the defaults provided by Tailwind: 190 | 191 | | Key | Classes | Reference | 192 | | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | 193 | | **`measure`** | `.measure-{none, 1, 2, 3, 4, 5, 6}` | [USWDS](https://designsystem.digital.gov/utilities/paragraph-styles/#max-width) | 194 | | **`fontFeatureSettings`** | `.text-{tabular, no-tabular, }` | [USWDS](https://designsystem.digital.gov/utilities/text-styles/#font-feature-settings) | 195 | | **`textIndent`** | `.text-indent-{-9, -8, -7, -6, -5, -4, -3, -205, -2, -105, -1, -05, -2px, -1px, 0, 1px, 2px, 05, 1, 105, 2, 205, 3, 4, 5, 6, 7, 8, 9 }` | [USWDS](https://designsystem.digital.gov/utilities/paragraph-styles/#text-indent) | 196 | 197 | ### Colors 198 | 199 | By default, `tailwindcss-uswds` provides [theme](https://designsystem.digital.gov/design-tokens/color/theme-tokens/), [state](https://designsystem.digital.gov/design-tokens/color/state-tokens/), and basic color tokens to all color based Tailwind utilities (`.bg-{color}`, `.border-{color}`, and `.text-{color}`). By using the value of `extended` as outlined in the [overrides](#overrides-optional) section, [system](https://designsystem.digital.gov/design-tokens/color/system-tokens/) tokens can be included as well. Do note that including system color tokens will add hundreds of additional colors (and thousands of Tailwind generated utilities), so be sure to use the [purge](https://tailwindcss.com/docs/controlling-file-size/#removing-unused-css) option provided by Tailwind to remove unused CSS. 200 | 201 | #### Default color tokens 202 | 203 | ```js 204 | // tailwind.config.js 205 | module.exports = { 206 | theme: { 207 | // ... 208 | colors: { 209 | transparent: "rgba(0, 0, 0, 0)", 210 | black: "rgb(0, 0, 0)", // #000000 211 | white: "rgb(255, 255, 255)", // #ffffff 212 | 213 | // Theme color tokens 214 | base: { 215 | lightest: "rgb(240, 240, 240)", // #f0f0f0 216 | lighter: "rgb(223, 225, 226)", // #dfe1e2 217 | light: "rgb(169, 174, 177)", // #a9aeb1 218 | default: "rgb(113, 118, 122)", // #71767a 219 | dark: "rgb(86, 92, 101)", // #565c65 220 | darker: "rgb(61, 69, 81)", // #3d4551 221 | darkest: "rgb(27, 27, 27)" // #1b1b1b 222 | }, 223 | ink: "rgb(27, 27, 27)", // #1b1b1b 224 | 225 | primary: { 226 | lighter: "rgb(217, 232, 246)", // #d9e8f6 227 | light: "rgb(115, 179, 231)", // #73b3e7 228 | default: "rgb(0, 94, 162)", // #005ea2 229 | vivid: "rgb(0, 80, 216)", // #0050d8 230 | dark: "rgb(26, 68, 128)", // #1a4480 231 | darker: "rgb(22, 46, 81)" // #162e51 232 | }, 233 | secondary: { 234 | lighter: "rgb(243, 225, 228)", // #f8dfe2 235 | light: "rgb(242, 147, 140)", // #f2938c 236 | default: "rgb(216, 57, 51)", // #d83933 237 | vivid: "rgb(228, 29, 61)", // #e41d3d 238 | dark: "rgb(181, 9, 9)", // #b50909 239 | darker: "rgb(139, 10, 3)" // #8b0a03 240 | }, 241 | "accent-cool": { 242 | darker: "rgb(7, 100, 141)", // #e1f3f8 243 | dark: "rgb(40, 160, 203)", // #97d4ea 244 | default: "rgb(0, 189, 227)", // #00bde3 245 | light: "rgb(151, 212, 234)", // #28a0cb 246 | lighter: "rgb(225, 243, 248)" // #07648d 247 | }, 248 | "accent-warm": { 249 | lighter: "rgb(242, 228, 212)", // #f2e4d4 250 | light: "rgb(255, 188, 120)", // #ffbc78 251 | default: "rgb(250, 148, 65)", // #fa9441 252 | dark: "rgb(192, 86, 0)", // #c05600 253 | darker: "rgb(119, 85, 64)", // #775540 254 | }, 255 | 256 | // State color tokens 257 | info: { 258 | lighter: "rgb(231, 246, 248)", // #e7f6f8 259 | light: "rgb(153, 222, 234)", // #99deea 260 | default: "rgb(0, 189, 227)", // #00bde3 261 | dark: "rgb(0, 158, 193)", // #009ec1 262 | darker: "rgb(46, 98, 118)" // #2e6276 263 | }, 264 | error: { 265 | lighter: "rgb(244, 227, 219)", // #f4e3db 266 | light: "rgb(243, 146, 104)", // #f39268 267 | default: "rgb(213, 67, 9)", // #d54309 268 | dark: "rgb(181, 9, 9)", // #b50909 269 | darker: "rgb(111, 51, 49)" // #6f3331 270 | 271 | }, 272 | warning: { 273 | lighter: "rgb(250, 243, 209)", // #faf3d1 274 | light: "rgb(254, 230, 133)", // #fee685 275 | default: "rgb(255, 190, 46)", // #ffbe2e 276 | dark: "rgb(229, 160, 0)", // #e5a000 277 | darker: "rgb(147, 111, 56)" // #936f38 278 | }, 279 | success: { 280 | lighter: "rgb(236, 243, 236)", // #ecf3ec 281 | light: "rgb(112, 225, 123)", // #70e17b 282 | default: "rgb(0, 169, 28)", // #00a91c 283 | dark: "rgb(77, 128, 85)", // #4d8055 284 | darker: "rgb(68, 100, 67)" // #446443 285 | }, 286 | disabled: { 287 | light: "rgb(230, 230, 230)", // #e6e6e6 288 | default: "rgb(201, 201, 201)", // #c9c9c9 289 | dark: "rgb(173, 173, 173)" // #adadad 290 | }, 291 | 292 | // Basic color tokens 293 | red: "rgb(229, 34, 7)", // #e52207 294 | orange: "rgb(230, 111, 14)", // #e66f0e 295 | gold: "rgb(255, 190, 46)", // #ffbe2e 296 | yellow: "rgb(254, 230, 133)", // #fee685 297 | green: "rgb(83, 130, 0)", // #538200 298 | mint: "rgb(4, 197, 133)", // #04c585 299 | cyan: "rgb(0, 158, 193)", // #009ec1 300 | blue: "rgb(0, 118, 214)", // #0076d6 301 | indigo: "rgb(103, 108, 200)", // #676cc8 302 | violet: "rgb(129, 104, 179)", // #8168b3 303 | magenta: "rgb(215, 45, 121)" // #d72d79 304 | } 305 | // ... 306 | }, 307 | plugins: [require("@hursey013/tailwindcss-uswds")] 308 | }; 309 | ``` 310 | 311 | #### Customizing colors 312 | 313 | The USWDS based theme can be customized by using your project's `tailwind.config.js` file and following the process outlined in the [customizing colors](https://tailwindcss.com/docs/customizing-colors/) docs. 314 | 315 | To reference [system](https://designsystem.digital.gov/design-tokens/color/system-tokens/) color tokens directly by name (without needing to use the `extended` option) you can import the JSON file generated by `tailwindcss-uswds`: 316 | 317 | ```js 318 | // tailwind.config.js 319 | const colors = require("@hursey013/tailwindcss-uswds/dist/colors"); 320 | 321 | module.exports = { 322 | theme: { 323 | // ... 324 | extend: { 325 | colors: { 326 | // ... 327 | primary: { 328 | ...colors.primary, 329 | vivid: colors["blue-warm"].50v 330 | } 331 | // ... 332 | } 333 | } 334 | }, 335 | plugins: [require("@hursey013/tailwindcss-uswds")] 336 | }; 337 | ``` 338 | 339 | ### Breakpoints 340 | 341 | The default breakpoints provided by USWDS are as follows: 342 | 343 | ```js 344 | // tailwind.config.js 345 | module.exports = { 346 | theme: { 347 | // ... 348 | screens: { 349 | card: "10rem", 350 | "card-lg": "15rem", 351 | desktop: "64rem", 352 | "desktop-lg": "75rem", 353 | mobile: "20rem", 354 | "mobile-lg": "30rem", 355 | tablet: "40rem", 356 | "tablet-lg": "55rem", 357 | widescreen: "87.5rem" 358 | } 359 | // ... 360 | }, 361 | plugins: [require("@hursey013/tailwindcss-uswds")] 362 | }; 363 | ``` 364 | 365 | These breakpoints can be used with most utility classes by adding the corresponding prefix: 366 | 367 | ```html 368 | 369 | 370 | // ... 371 | ``` 372 | 373 | ### Icons 374 | 375 | As an added convenience, all icons provided by USWDS are included in `tailwindcss-uswds`. 376 | 377 | ```js 378 | import flag from "@hursey013/tailwindcss-uswds/dist/img/us_flag_small.png"; 379 | 380 | // ... 381 | ``` 382 | -------------------------------------------------------------------------------- /dist/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | !.npmignore 4 | -------------------------------------------------------------------------------- /dist/.npmignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hursey013/tailwindcss-uswds/fe1ed7dc4f0d04a2889c2b629f7949ac42ffda7f/dist/.npmignore -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hursey013/tailwindcss-uswds", 3 | "version": "1.2.0", 4 | "description": "U.S. Tailwind Design System", 5 | "main": "src/index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/hursey013/tailwindcss-uswds.git" 9 | }, 10 | "author": "Brian Hurst", 11 | "license": "MIT", 12 | "scripts": { 13 | "build": "node scripts/build.js && npm run copy", 14 | "copy:fonts": "cp -R node_modules/uswds/dist/fonts dist", 15 | "copy:img": "cp -R node_modules/uswds/dist/img dist", 16 | "copy": "npm run copy:fonts && npm run copy:img", 17 | "prepare": "npm run build", 18 | "release": "dotenv release-it" 19 | }, 20 | "peerDependencies": { 21 | "tailwindcss": "^1.0" 22 | }, 23 | "dependencies": { 24 | "node-sass": "^4.14.1", 25 | "sass-extract": "^2.1.0", 26 | "sass-extract-js": "^0.4.0", 27 | "tailwindcss": "^1.4.6", 28 | "uswds": "2.8" 29 | }, 30 | "bugs": { 31 | "url": "https://github.com/hursey013/tailwindcss-uswds/issues" 32 | }, 33 | "homepage": "https://github.com/hursey013/tailwindcss-uswds#readme", 34 | "devDependencies": { 35 | "dotenv-cli": "^3.2.0", 36 | "release-it": "^13.6.5" 37 | }, 38 | "release-it": { 39 | "github": { 40 | "release": true 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /scripts/build.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const sassExtract = require("sass-extract"); 4 | require("sass-extract/lib/plugins/filter"); 5 | require("sass-extract-js"); 6 | 7 | const noCamelCase = ["accent-cool", "accent-warm"]; 8 | const removedPrefixes = ["ls-", "neg"]; 9 | const removedProps = [ 10 | "background-color", 11 | "border-color", 12 | "color", 13 | "function", 14 | "outline-color", 15 | "palette-color", 16 | "text-decoration-color" 17 | ]; 18 | const renamedProps = { breakpoints: "screens", noValue: "default" }; 19 | 20 | const removePrefix = (string, source) => { 21 | const regex = new RegExp(source.join("|"), "gi"); 22 | return string.replace(regex, ""); 23 | }; 24 | 25 | const renameProp = (key, source) => (source[key] ? source[key] : key); 26 | 27 | const stringToCamelCase = string => 28 | !noCamelCase.includes(string) 29 | ? string.replace(/-([a-z])/g, g => g[1].toUpperCase()) 30 | : string; 31 | 32 | const parseFonts = obj => { 33 | return Object.keys(obj) 34 | .filter(key => obj[key].src) 35 | .reduce((acc, key) => { 36 | const { ["display-name"]: family, src } = obj[key]; 37 | 38 | Object.keys(src) 39 | .filter(style => style !== "dir") 40 | .forEach(style => { 41 | const weight = src[style]; 42 | const array = Object.keys(weight) 43 | .filter(key => weight[key]) 44 | .map(key => ({ 45 | dir: src.dir, 46 | family, 47 | file: weight[key], 48 | style: style === "roman" ? "normal" : style, 49 | weight: key 50 | })); 51 | acc.push(...array); 52 | }); 53 | 54 | return acc; 55 | }, []); 56 | }; 57 | 58 | const parseValues = obj => { 59 | return Object.keys(obj) 60 | .filter(key => (obj[key] || obj[key] === 0) && !removedProps.includes(key)) 61 | .reduce((acc, key) => { 62 | const newKey = removePrefix( 63 | renameProp(key, renamedProps), 64 | removedPrefixes 65 | ); 66 | if (typeof obj[key] === "object") { 67 | if (obj[key].slug) { 68 | if (obj[key].content) { 69 | acc[obj[newKey].slug] = obj[key].content; 70 | } 71 | } else { 72 | acc[stringToCamelCase(newKey)] = parseValues(obj[key]); 73 | } 74 | } else { 75 | acc[newKey] = obj[key].toString(); 76 | } 77 | 78 | return acc; 79 | }, {}); 80 | }; 81 | 82 | const unflattenColors = obj => { 83 | return Object.keys(obj).reduce((acc, key) => { 84 | const array = key.split("-"); 85 | const value = array.pop(); 86 | const newKey = array.join("-"); 87 | 88 | return { ...acc, [newKey]: { ...acc[newKey], [value]: obj[key] } }; 89 | }, {}); 90 | }; 91 | 92 | sassExtract 93 | .render( 94 | { 95 | file: require.resolve("uswds/src/stylesheets/uswds.scss") 96 | }, 97 | { 98 | plugins: [ 99 | { 100 | plugin: "filter", 101 | options: { 102 | only: { 103 | props: [ 104 | "$all-project-colors", 105 | "$project-font-weights", 106 | "$system-properties", 107 | "$system-typeface-tokens", 108 | "$tokens-color-basic", 109 | "$tokens-color-required", 110 | "$tokens-color-state", 111 | "$tokens-color-system", 112 | "$tokens-color-theme", 113 | "$tokens-font-system", 114 | "$tokens-font-theme" 115 | ] 116 | } 117 | } 118 | }, 119 | { plugin: "sass-extract-js", options: { camelCase: false } } 120 | ] 121 | } 122 | ) 123 | .then(rendered => { 124 | const { 125 | allProjectColors, 126 | projectFontWeights, 127 | systemProperties, 128 | systemTypefaceTokens, 129 | tokensColorBasic, 130 | tokensColorRequired, 131 | tokensColorState, 132 | tokensColorSystem, 133 | tokensColorTheme, 134 | tokensFontSystem, 135 | tokensFontTheme 136 | } = parseValues(rendered.vars); 137 | 138 | const colors = unflattenColors(tokensColorSystem); 139 | 140 | const props = { 141 | ...systemProperties, 142 | borderWidth: { 143 | standard: { 144 | ...systemProperties.borderWidth.standard, 145 | ...systemProperties.border.standard 146 | }, 147 | extended: { 148 | ...systemProperties.borderWidth.extended, 149 | ...systemProperties.border.extended 150 | } 151 | }, 152 | colors: { 153 | standard: { 154 | ...allProjectColors, 155 | ...tokensColorBasic, 156 | ...tokensColorRequired 157 | }, 158 | extended: colors 159 | }, 160 | fontSize: { 161 | standard: tokensFontTheme, 162 | extended: tokensFontSystem 163 | }, 164 | fontWeight: { 165 | standard: projectFontWeights, 166 | extended: systemProperties.fontWeight.extended 167 | }, 168 | margin: { 169 | standard: { 170 | ...systemProperties.margin.standard, 171 | ...systemProperties.marginHorizontal.standard, 172 | ...systemProperties.marginVertical.standard 173 | }, 174 | extended: { 175 | ...systemProperties.margin.extended, 176 | ...systemProperties.marginHorizontal.extended, 177 | ...systemProperties.marginVertical.extended 178 | } 179 | } 180 | }; 181 | 182 | return { colors, fonts: parseFonts(systemTypefaceTokens), props }; 183 | }) 184 | .then(res => { 185 | const { colors, fonts, props } = res; 186 | Object.keys(res).forEach(key => { 187 | fs.writeFileSync(`dist/${key}.json`, JSON.stringify(res[key])); 188 | }); 189 | }); 190 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const plugin = require("tailwindcss/plugin"); 2 | const fonts = require("../dist/fonts.json"); 3 | const props = require("../dist/props.json"); 4 | 5 | const defaultOptions = { 6 | fontPath: "~@hursey013/tailwindcss-uswds/dist/fonts", 7 | overrides: { 8 | borderRadius: "standard", 9 | borderWidth: "standard", 10 | boxShadow: "standard", 11 | colors: "standard", 12 | cursor: "standard", 13 | flex: "standard", 14 | fontFamily: "standard", 15 | fontFeatureSettings: "standard", 16 | fontSize: "standard", 17 | fontWeight: "standard", 18 | gap: "standard", 19 | height: "standard", 20 | letterSpacing: "standard", 21 | lineHeight: "standard", 22 | margin: "standard", 23 | maxHeight: "standard", 24 | maxWidth: "standard", 25 | measure: "standard", 26 | minHeight: "standard", 27 | minWidth: "standard", 28 | opacity: "standard", 29 | order: "standard", 30 | padding: "standard", 31 | screens: "standard", 32 | textIndent: "standard", 33 | width: "standard", 34 | zIndex: "standard" 35 | } 36 | }; 37 | 38 | module.exports = plugin.withOptions( 39 | function(options = {}) { 40 | const opts = { 41 | ...defaultOptions, 42 | ...options, 43 | overrides: { 44 | ...defaultOptions.overrides, 45 | ...options.overrides 46 | } 47 | }; 48 | 49 | return function({ addBase, addComponents, addUtilities, e, theme }) { 50 | const base = [ 51 | { 52 | body: { 53 | backgroundColor: theme("colors.white"), 54 | color: theme("colors.ink"), 55 | overflowX: "hidden" 56 | } 57 | }, 58 | ...fonts 59 | .filter(font => 60 | Object.values(theme("fontWeight")).includes(font.weight) 61 | ) 62 | .map(font => ({ 63 | "@font-face": { 64 | fontFamily: font.family, 65 | fontStyle: font.style, 66 | fontWeight: font.weight, 67 | fontDisplay: "fallback", 68 | src: `url(${opts.fontPath}/${font.dir}/${font.file}.woff2) format("woff2"), 69 | url(${opts.fontPath}/${font.dir}/${font.file}.woff) format("woff"), 70 | url(${opts.fontPath}/${font.dir}/${font.file}.ttf) format("truetype")` 71 | } 72 | })), 73 | ...Object.keys(theme("fontFamily")).map(key => ({ 74 | [`[class*=${e(`text-${key}`)}]`]: { 75 | fontFamily: theme("fontFamily") 76 | [key].split(", ") 77 | .map(s => (s.includes(" ") ? `'${s}'` : s)) 78 | } 79 | })) 80 | ]; 81 | 82 | addBase(base); 83 | 84 | const uMeasure = opts.overrides.measure 85 | ? Object.keys(theme("measure")).map(key => ({ 86 | [`.${e(`measure-${key}`)}`]: { maxWidth: theme("measure")[key] } 87 | })) 88 | : {}; 89 | const uTabular = opts.overrides.fontFeatureSettings 90 | ? Object.keys(theme("fontFeatureSettings")).map(key => ({ 91 | [`.${e(`text-${key}`)}`]: { 92 | fontFeatureSettings: theme("fontFeatureSettings")[key] 93 | } 94 | })) 95 | : {}; 96 | const uTextIndent = opts.overrides.textIndent 97 | ? Object.keys(theme("textIndent")).map(key => ({ 98 | [`.${e( 99 | key.startsWith("-") 100 | ? `-text-indent-${key.slice(1)}` 101 | : `text-indent-${key}` 102 | )}`]: { 103 | textIndent: theme("textIndent")[key] 104 | } 105 | })) 106 | : {}; 107 | 108 | addUtilities([uMeasure, uTabular, uTextIndent], { 109 | variants: ["responsive"] 110 | }); 111 | }; 112 | }, 113 | function(options = {}) { 114 | const opts = { 115 | ...defaultOptions, 116 | ...options, 117 | overrides: { 118 | ...defaultOptions.overrides, 119 | ...options.overrides 120 | } 121 | }; 122 | 123 | return { 124 | theme: Object.keys(opts.overrides).reduce((acc, key) => { 125 | const override = opts.overrides[key]; 126 | 127 | if (override) { 128 | acc[key] = { 129 | ...props[key].standard, 130 | ...(override === "extended" ? props[key].extended : {}) 131 | }; 132 | } 133 | 134 | return acc; 135 | }, {}) 136 | }; 137 | } 138 | ); 139 | --------------------------------------------------------------------------------