├── .gitignore ├── .release-it.json ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── index.js ├── package-lock.json ├── package.json └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "git": { 3 | "tagName": "v${version}", 4 | "requireCleanWorkingDir": false 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project mostly adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [3.1.0] - 2020-05-09 9 | 10 | ### Added 11 | - Added `line-[style]` and `line-[color]` utilities to go with Tailwind’s text decoration utilities (`underline` and `line-through`); they can be customized with the `textDecorationStyle` and `textDecorationColor` theme keys 12 | 13 | ### Fixed 14 | - Fixed an issue when using a font size that includes a default line height in a text style (e.g. `['16px', '24px']`), which is supported since Tailwind 1.3 15 | 16 | ## [3.0.1] - 2020-02-13 17 | 18 | ### Fixed 19 | - Negative text indent utilities now have the expected class name (`-indent-*` instead of `indent--*`) 20 | 21 | ## [3.0.0] - 2020-02-05 22 | 23 | ### Added 24 | - Added kerning utilities, which can be disabled by setting the `kerning` option to `false` (thanks, [@mapgrid](https://github.com/mapgrid)!) 25 | - Added text rendering utilities, which can be customized with the `textRendering` theme object (thanks, [@mapgrid](https://github.com/mapgrid)!) 26 | 27 | ### Changed 28 | - Changed to use Tailwind 1.2’s new plugin definition syntax 29 | - Font variant utilities (caps, nums, and ligatures) are now generated from the `fontVariantCaps`, `fontVariantNumeric`, and `fontVariantLigatures` theme keys, and their names have changed to include a `caps-`, `nums-`, or `ligatures-` prefix (so `normal-caps` is now `caps-normal`, `no-ligatures` is now `ligatures-none`, etc.) 30 | - The `caps`, `nums`, and `ligatures` variants keys have changed to `fontVariantCaps`, `fontVariantNumeric`, and `fontVariantLigatures` 31 | 32 | ### Removed 33 | - Removed the `caps`, `nums`, and `ligatures` options 34 | 35 | ## [2.2.0] - 2019-09-02 36 | 37 | ### Added 38 | - Added utilities for some OpenType features: caps (alternate glyphs for capital letters), nums (alternate glyphs for numbers, fractions, and ordinal markers), and ligatures 39 | - Added a `no-ellipsis` utility to undo `ellipsis` at breakpoints 40 | 41 | ## [2.1.1] - 2019-05-27 42 | 43 | ### Fixed 44 | - Fixed an issue when using an array for a font family in a text style 45 | 46 | ## [2.1.0] - 2019-05-26 47 | 48 | ### Added 49 | - Added text style components (see the `textStyles` theme object in the `README` for more info) 50 | 51 | ## [2.0.0] - 2019-05-13 52 | 53 | ### Changed since 2.0.0-beta.2 54 | - Added support for global variants thanks to Tailwind’s `variants()` helper function 55 | 56 | ### Added since 1.x 57 | - Tailwind 1.0.0 compatibility 58 | - Added text unset utilities 59 | - Added boolean options to enable/disable the non-configurable utilities (`ellipsis`, `hyphens`, and `textUnset`) 60 | 61 | ### Changed since 1.x 62 | - Most of the config options have been moved to the `theme` and `variants` objects in your Tailwind config (see `README` for more info) 63 | - Responsive variants are now generated by default 64 | 65 | ## [2.0.0-beta.2] - 2019-04-04 66 | 67 | ### Added 68 | - Added text unset utilities 69 | - Added boolean options to enable/disable the non-configurable utilities (`ellipsis`, `hyphens`, and `textUnset`) 70 | 71 | ## [2.0.0-beta.1] - 2019-04-04 72 | 73 | ### Added 74 | - Tailwind 1.0.0 compatibility 75 | 76 | ### Changed 77 | - Most of the config options have been moved to the `theme` and `variants` objects in your Tailwind config (see `README` for more info) 78 | - Responsive variants are now generated by default 79 | 80 | ## [1.1.0] - 2019-03-22 81 | 82 | ### Added 83 | - Added the `hyphens-none` and `hyphens-manual` utilities 84 | 85 | ### Changed 86 | - Changed the `hyphens` utility to `hyphens-auto` 87 | 88 | ## [1.0.2] - 2018-11-04 89 | 90 | ### Added 91 | - Added proper tests with Jest 92 | 93 | ## [1.0.1] - 2018-08-14 94 | 95 | ### Fixed 96 | - Fixed escaping in selectors generated by the plugin 97 | 98 | ## [1.0.0] - 2018-05-06 99 | 100 | Initial release 101 | 102 | [Unreleased]: https://github.com/benface/tailwindcss-typography/compare/v3.1.0...HEAD 103 | [3.1.0]: https://github.com/benface/tailwindcss-typography/compare/v3.0.1...v3.1.0 104 | [3.0.1]: https://github.com/benface/tailwindcss-typography/compare/v3.0.0...v3.0.1 105 | [3.0.0]: https://github.com/benface/tailwindcss-typography/compare/v2.2.0...v3.0.0 106 | [2.2.0]: https://github.com/benface/tailwindcss-typography/compare/v2.1.1...v2.2.0 107 | [2.1.1]: https://github.com/benface/tailwindcss-typography/compare/v2.1.0...v2.1.1 108 | [2.1.0]: https://github.com/benface/tailwindcss-typography/compare/v2.0.0...v2.1.0 109 | [2.0.0]: https://github.com/benface/tailwindcss-typography/compare/v2.0.0-beta.2...v2.0.0 110 | [2.0.0-beta.2]: https://github.com/benface/tailwindcss-typography/compare/v2.0.0-beta.1...v2.0.0-beta.2 111 | [2.0.0-beta.1]: https://github.com/benface/tailwindcss-typography/compare/v1.1.0...v2.0.0-beta.1 112 | [1.1.0]: https://github.com/benface/tailwindcss-typography/compare/v1.0.2...v1.1.0 113 | [1.0.2]: https://github.com/benface/tailwindcss-typography/compare/v1.0.1...v1.0.2 114 | [1.0.1]: https://github.com/benface/tailwindcss-typography/compare/v1.0.0...v1.0.1 115 | [1.0.0]: https://github.com/benface/tailwindcss-typography/releases/tag/v1.0.0 116 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # ISC License 2 | 3 | Copyright (c) Benoît Rouleau 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ⛔️ DEPRECATED 2 | 3 | Tailwind CSS has an official [`typography` plugin](https://tailwindcss.com/docs/typography-plugin) that you should use instead of this one. It may not support _all_ the features that this one has (its main use case is styling “vanilla HTML”), but it works with the latest versions of Tailwind, which also support a lot more typography utilities ([`indent`](https://tailwindcss.com/docs/text-indent), [`font-variant-numeric`](https://tailwindcss.com/docs/font-variant-numeric), text decoration color, style, and thickness, etc.). 4 | 5 | # Typography Plugin for Tailwind CSS 6 | 7 | ## Requirements 8 | 9 | This plugin requires Tailwind CSS 1.2 or later. If your project uses an older version of Tailwind, you should install the latest 2.x version of this plugin (`npm install tailwindcss-typography@2.x`). 10 | 11 | ## Installation 12 | 13 | ```bash 14 | npm install tailwindcss-typography 15 | ``` 16 | 17 | ## Usage 18 | 19 | ```js 20 | // tailwind.config.js 21 | module.exports = { 22 | theme: { 23 | textIndent: { // defaults to {} 24 | '1': '0.25rem', 25 | '2': '0.5rem', 26 | }, 27 | textShadow: { // defaults to {} 28 | 'default': '0 2px 5px rgba(0, 0, 0, 0.5)', 29 | 'lg': '0 2px 10px rgba(0, 0, 0, 0.5)', 30 | }, 31 | textDecorationStyle: { // defaults to these values 32 | 'solid': 'solid', 33 | 'double': 'double', 34 | 'dotted': 'dotted', 35 | 'dashed': 'dashed', 36 | 'wavy': 'wavy', 37 | }, 38 | textDecorationColor: { // defaults to theme => theme('colors') 39 | 'red': '#f00', 40 | 'green': '#0f0', 41 | 'blue': '#00f', 42 | }, 43 | fontVariantCaps: { // defaults to these values 44 | 'normal': 'normal', 45 | 'small': 'small-caps', 46 | 'all-small': 'all-small-caps', 47 | 'petite': 'petite-caps', 48 | 'unicase': 'unicase', 49 | 'titling': 'titling-caps', 50 | }, 51 | fontVariantNumeric: { // defaults to these values 52 | 'normal': 'normal', 53 | 'ordinal': 'ordinal', 54 | 'slashed-zero': 'slashed-zero', 55 | 'lining': 'lining-nums', 56 | 'oldstyle': 'oldstyle-nums', 57 | 'proportional': 'proportional-nums', 58 | 'tabular': 'tabular-nums', 59 | 'diagonal-fractions': 'diagonal-fractions', 60 | 'stacked-fractions': 'stacked-fractions', 61 | }, 62 | fontVariantLigatures: { // defaults to these values 63 | 'normal': 'normal', 64 | 'none': 'none', 65 | 'common': 'common-ligatures', 66 | 'no-common': 'no-common-ligatures', 67 | 'discretionary': 'discretionary-ligatures', 68 | 'no-discretionary': 'no-discretionary-ligatures', 69 | 'historical': 'historical-ligatures', 70 | 'no-historical': 'no-historical-ligatures', 71 | 'contextual': 'contextual', 72 | 'no-contextual': 'no-contextual', 73 | }, 74 | textRendering: { // defaults to these values 75 | 'rendering-auto': 'auto', 76 | 'optimize-legibility': 'optimizeLegibility', 77 | 'optimize-speed': 'optimizeSpeed', 78 | 'geometric-precision': 'geometricPrecision' 79 | }, 80 | textStyles: theme => ({ // defaults to {} 81 | heading: { 82 | output: false, // this means there won't be a "heading" component in the CSS, but it can be extended 83 | fontWeight: theme('fontWeight.bold'), 84 | lineHeight: theme('lineHeight.tight'), 85 | }, 86 | h1: { 87 | extends: 'heading', // this means all the styles in "heading" will be copied here; "extends" can also be an array to extend multiple text styles 88 | fontSize: theme('fontSize.5xl'), 89 | '@screen sm': { 90 | fontSize: theme('fontSize.6xl'), 91 | }, 92 | }, 93 | h2: { 94 | extends: 'heading', 95 | fontSize: theme('fontSize.4xl'), 96 | '@screen sm': { 97 | fontSize: theme('fontSize.5xl'), 98 | }, 99 | }, 100 | h3: { 101 | extends: 'heading', 102 | fontSize: theme('fontSize.4xl'), 103 | }, 104 | h4: { 105 | extends: 'heading', 106 | fontSize: theme('fontSize.3xl'), 107 | }, 108 | h5: { 109 | extends: 'heading', 110 | fontSize: theme('fontSize.2xl'), 111 | }, 112 | h6: { 113 | extends: 'heading', 114 | fontSize: theme('fontSize.xl'), 115 | }, 116 | link: { 117 | fontWeight: theme('fontWeight.bold'), 118 | color: theme('colors.blue.400'), 119 | '&:hover': { 120 | color: theme('colors.blue.600'), 121 | textDecoration: 'underline', 122 | }, 123 | }, 124 | richText: { 125 | fontWeight: theme('fontWeight.normal'), 126 | fontSize: theme('fontSize.base'), 127 | lineHeight: theme('lineHeight.relaxed'), 128 | '> * + *': { 129 | marginTop: '1em', 130 | }, 131 | 'h1': { 132 | extends: 'h1', 133 | }, 134 | 'h2': { 135 | extends: 'h2', 136 | }, 137 | 'h3': { 138 | extends: 'h3', 139 | }, 140 | 'h4': { 141 | extends: 'h4', 142 | }, 143 | 'h5': { 144 | extends: 'h5', 145 | }, 146 | 'h6': { 147 | extends: 'h6', 148 | }, 149 | 'ul': { 150 | listStyleType: 'disc', 151 | }, 152 | 'ol': { 153 | listStyleType: 'decimal', 154 | }, 155 | 'a': { 156 | extends: 'link', 157 | }, 158 | 'b, strong': { 159 | fontWeight: theme('fontWeight.bold'), 160 | }, 161 | 'i, em': { 162 | fontStyle: 'italic', 163 | }, 164 | }, 165 | }), 166 | }, 167 | variants: { // all the following default to ['responsive'] 168 | textIndent: ['responsive'], 169 | textShadow: ['responsive'], 170 | textDecorationStyle: ['responsive'], 171 | textDecorationColor: ['responsive'], 172 | ellipsis: ['responsive'], 173 | hyphens: ['responsive'], 174 | kerning: ['responsive'], 175 | textUnset: ['responsive'], 176 | fontVariantCaps: ['responsive'], 177 | fontVariantNumeric: ['responsive'], 178 | fontVariantLigatures: ['responsive'], 179 | textRendering: ['responsive'], 180 | }, 181 | plugins: [ 182 | require('tailwindcss-typography')({ 183 | // all these options default to the values specified here 184 | ellipsis: true, // whether to generate ellipsis utilities 185 | hyphens: true, // whether to generate hyphenation utilities 186 | kerning: true, // whether to generate kerning utilities 187 | textUnset: true, // whether to generate utilities to unset text properties 188 | componentPrefix: 'c-', // the prefix to use for text style classes 189 | }), 190 | ], 191 | }; 192 | ``` 193 | 194 | This plugin generates the following utilities: 195 | 196 | ```css 197 | /* configurable with the "textIndent" theme object */ 198 | .indent-[key] { 199 | text-indent: [value]; 200 | } 201 | 202 | /* configurable with the "textShadow" theme object */ 203 | /* note: the "default" key generates a simple "text-shadow" class (instead of "text-shadow-default") */ 204 | .text-shadow-[key] { 205 | text-shadow: [value]; 206 | } 207 | 208 | /* configurable with the "textDecorationStyle" theme object */ 209 | .line-[key] { 210 | text-decoration-style: [value]; 211 | } 212 | 213 | /* configurable with the "textDecorationColor" theme object */ 214 | .line-[key] { 215 | text-decoration-color: [value]; 216 | } 217 | 218 | /* generated when the "ellipsis" option is set to true */ 219 | .ellipsis { 220 | text-overflow: ellipsis; 221 | } 222 | .no-ellipsis { 223 | text-overflow: clip; 224 | } 225 | 226 | /* generated when the "hyphens" option is set to true */ 227 | .hyphens-none { 228 | hyphens: none; 229 | } 230 | .hyphens-manual { 231 | hyphens: manual; 232 | } 233 | .hyphens-auto { 234 | hyphens: auto; 235 | } 236 | 237 | /* generated when the "kerning" option is set to true */ 238 | .kerning { 239 | font-kerning: normal; 240 | } 241 | .kerning-none { 242 | font-kerning: none; 243 | } 244 | .kerning-auto { 245 | font-kerning: auto; 246 | } 247 | 248 | /* generated when the "textUnset" option is set to true */ 249 | .font-family-unset { 250 | font-family: inherit; 251 | } 252 | .font-weight-unset { 253 | font-weight: inherit; 254 | } 255 | .font-style-unset { 256 | font-style: inherit; 257 | } 258 | .text-size-unset { 259 | font-size: inherit; 260 | } 261 | .text-align-unset { 262 | text-align: inherit; 263 | } 264 | .leading-unset { 265 | line-height: inherit; 266 | } 267 | .tracking-unset { 268 | letter-spacing: inherit; 269 | } 270 | .text-color-unset { 271 | color: inherit; 272 | } 273 | .text-transform-unset { 274 | text-transform: inherit; 275 | } 276 | 277 | /* configurable with the "fontVariantCaps" theme object */ 278 | .caps-normal { 279 | font-variant-caps: normal; 280 | } 281 | .caps-small { 282 | font-variant-caps: small-caps; 283 | } 284 | .caps-all-small { 285 | font-variant-caps: all-small-caps; 286 | } 287 | .caps-petite { 288 | font-variant-caps: petite-caps; 289 | } 290 | .caps-unicase { 291 | font-variant-caps: unicase; 292 | } 293 | .caps-titling { 294 | font-variant-caps: titling-caps; 295 | } 296 | 297 | /* configurable with the "fontVariantNumeric" theme object */ 298 | .nums-normal { 299 | font-variant-numeric: normal; 300 | } 301 | .nums-ordinal { 302 | font-variant-numeric: ordinal; 303 | } 304 | .nums-slashed-zero { 305 | font-variant-numeric: slashed-zero; 306 | } 307 | .nums-lining { 308 | font-variant-numeric: lining-nums; 309 | } 310 | .nums-oldstyle { 311 | font-variant-numeric: oldstyle-nums; 312 | } 313 | .nums-proportional { 314 | font-variant-numeric: proportional-nums; 315 | } 316 | .nums-tabular { 317 | font-variant-numeric: tabular-nums; 318 | } 319 | .nums-diagonal-fractions { 320 | font-variant-numeric: diagonal-fractions; 321 | } 322 | .nums-stacked-fractions { 323 | font-variant-numeric: stacked-fractions; 324 | } 325 | 326 | /* configurable with the "fontVariantLigatures" theme object */ 327 | .ligatures-normal { 328 | font-variant-ligatures: normal; 329 | } 330 | .ligatures-none { 331 | font-variant-ligatures: none; 332 | } 333 | .ligatures-common { 334 | font-variant-ligatures: common-ligatures; 335 | } 336 | .ligatures-no-common { 337 | font-variant-ligatures: no-common-ligatures; 338 | } 339 | .ligatures-discretionary { 340 | font-variant-ligatures: discretionary-ligatures; 341 | } 342 | .ligatures-no-discretionary { 343 | font-variant-ligatures: no-discretionary-ligatures; 344 | } 345 | .ligatures-historical { 346 | font-variant-ligatures: historical-ligatures; 347 | } 348 | .ligatures-no-historical { 349 | font-variant-ligatures: no-historical-ligatures; 350 | } 351 | .ligatures-contextual { 352 | font-variant-ligatures: contextual; 353 | } 354 | .ligatures-no-contextual { 355 | font-variant-ligatures: no-contextual; 356 | } 357 | 358 | /* configurable with the "textRendering" theme object */ 359 | .text-rendering-auto { 360 | text-rendering: auto; 361 | } 362 | .text-optimize-legibility { 363 | text-rendering: optimizeLegibility; 364 | } 365 | .text-optimize-speed { 366 | text-rendering: optimizeSpeed; 367 | } 368 | .text-geometric-precision { 369 | text-rendering: geometricPrecision; 370 | } 371 | ``` 372 | 373 | The plugin also generates components for text styles. The above config example would generate something like this: 374 | 375 | ```css 376 | .c-h1 { 377 | font-weight: 700; 378 | line-height: 1.25; 379 | font-size: 3rem; 380 | } 381 | @media (min-width: 640px) { 382 | .c-h1 { 383 | font-size: 4rem; 384 | } 385 | } 386 | .c-h2 { 387 | font-weight: 800; 388 | line-height: 1.25; 389 | font-size: 2.25rem; 390 | } 391 | @media (min-width: 640px) { 392 | .c-h2 { 393 | font-size: 3rem; 394 | } 395 | } 396 | .c-h3 { 397 | font-weight: 700; 398 | line-height: 1.25; 399 | font-size: 2.25rem; 400 | } 401 | .c-h4 { 402 | font-weight: 700; 403 | line-height: 1.25; 404 | font-size: 1.875rem; 405 | } 406 | .c-h5 { 407 | font-weight: 700; 408 | line-height: 1.25; 409 | font-size: 1.5rem; 410 | } 411 | .c-h6 { 412 | font-weight: 700; 413 | line-height: 1.25; 414 | font-size: 1.25rem; 415 | } 416 | 417 | .c-link { 418 | font-weight: 700; 419 | color: #63b3ed; 420 | } 421 | .c-link:hover { 422 | color: #3182ce; 423 | text-decoration: underline; 424 | } 425 | 426 | .c-rich-text { 427 | font-weight: 400; 428 | font-size: 1rem; 429 | line-height: 1.625; 430 | } 431 | .c-rich-text > * + * { 432 | margin-top: 1em; 433 | } 434 | .c-rich-text h1 { 435 | font-weight: 700; 436 | line-height: 1.25; 437 | font-size: 3rem; 438 | } 439 | @media (min-width: 640px) { 440 | .c-rich-text h1 { 441 | font-size: 4rem; 442 | } 443 | } 444 | .c-rich-text h2 { 445 | font-weight: 800; 446 | line-height: 1.25; 447 | font-size: 2.25rem; 448 | } 449 | @media (min-width: 640px) { 450 | .c-rich-text h2 { 451 | font-size: 3rem; 452 | } 453 | } 454 | .c-rich-text h3 { 455 | font-weight: 700; 456 | line-height: 1.25; 457 | font-size: 2.25rem; 458 | } 459 | .c-rich-text h4 { 460 | font-weight: 700; 461 | line-height: 1.25; 462 | font-size: 1.875rem; 463 | } 464 | .c-rich-text h5 { 465 | font-weight: 700; 466 | line-height: 1.25; 467 | font-size: 1.5rem; 468 | } 469 | .c-rich-text h6 { 470 | font-weight: 700; 471 | line-height: 1.25; 472 | font-size: 1.25rem; 473 | } 474 | .c-rich-text ul { 475 | list-style-type: disc; 476 | } 477 | .c-rich-text ol { 478 | list-style-type: decimal; 479 | } 480 | .c-rich-text a { 481 | font-weight: 700; 482 | color: #63b3ed; 483 | } 484 | .c-rich-text a:hover { 485 | color: #3182ce; 486 | text-decoration: underline; 487 | } 488 | .c-rich-text b, .c-rich-text strong { 489 | font-weight: 700; 490 | } 491 | .c-rich-text i, .c-rich-text em { 492 | font-style: italic; 493 | } 494 | ``` 495 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const plugin = require('tailwindcss/plugin'); 2 | const _ = require('lodash'); 3 | 4 | const defaultOptions = { 5 | ellipsis: true, 6 | hyphens: true, 7 | kerning: true, 8 | textUnset: true, 9 | componentPrefix: 'c-', 10 | }; 11 | 12 | const prefixNegativeModifiers = function(base, modifier) { 13 | return _.startsWith(modifier, '-') ? `-${base}-${modifier.slice(1)}` : `${base}-${modifier}`; 14 | }; 15 | 16 | const flattenColorPalette = function(colors) { 17 | return _(colors) 18 | .flatMap((color, name) => { 19 | if (!_.isPlainObject(color)) { 20 | return [[name, color]]; 21 | } 22 | return _.map(color, (value, key) => { 23 | const suffix = key === 'default' ? '' : `-${key}`; 24 | return [`${name}${suffix}`, value]; 25 | }); 26 | }) 27 | .fromPairs() 28 | .value(); 29 | }; 30 | 31 | const camelCaseToKebabCase = function(string) { 32 | return string 33 | .replace(/([a-z0-9])([A-Z])/g, '$1-$2') 34 | .replace(/([A-Z])([A-Z])(?=[a-z])/g, '$1-$2') 35 | .toLowerCase(); 36 | }; 37 | 38 | module.exports = plugin.withOptions(function(options = {}) { 39 | return function({ theme, variants, e, addUtilities, addComponents }) { 40 | options = _.defaults({}, options, defaultOptions); 41 | 42 | const textIndentUtilities = _.fromPairs( 43 | _.map(theme('textIndent'), (value, modifier) => { 44 | return [ 45 | `.${e(prefixNegativeModifiers('indent', modifier))}`, 46 | { 47 | textIndent: value, 48 | }, 49 | ]; 50 | }) 51 | ); 52 | 53 | const textShadowUtilities = _.fromPairs( 54 | _.map(theme('textShadow'), (value, modifier) => { 55 | return [ 56 | `.${e(`text-shadow${modifier === 'default' ? '' : `-${modifier}`}`)}`, 57 | { 58 | textShadow: value, 59 | }, 60 | ]; 61 | }) 62 | ); 63 | 64 | const textDecorationStyleUtilities = _.fromPairs( 65 | _.map(theme('textDecorationStyle'), (value, modifier) => { 66 | return [ 67 | `.${e(`line-${modifier}`)}`, 68 | { 69 | textDecorationStyle: value, 70 | }, 71 | ]; 72 | }) 73 | ); 74 | 75 | const textDecorationColorUtilities = _.fromPairs( 76 | _.map(flattenColorPalette(theme('textDecorationColor')), (value, modifier) => { 77 | return [ 78 | `.${e(`line-${modifier}`)}`, 79 | { 80 | textDecorationColor: value, 81 | }, 82 | ]; 83 | }) 84 | ); 85 | 86 | const ellipsisUtilities = options.ellipsis ? { 87 | '.ellipsis': { 88 | textOverflow: 'ellipsis', 89 | }, 90 | '.no-ellipsis': { 91 | textOverflow: 'clip', 92 | }, 93 | } : {}; 94 | 95 | const hyphensUtilities = options.hyphens ? { 96 | '.hyphens-none': { 97 | hyphens: 'none', 98 | }, 99 | '.hyphens-manual': { 100 | hyphens: 'manual', 101 | }, 102 | '.hyphens-auto': { 103 | hyphens: 'auto', 104 | }, 105 | } : {}; 106 | 107 | const kerningUtilities = options.kerning ? { 108 | '.kerning': { 109 | fontKerning: 'normal', 110 | }, 111 | '.kerning-none': { 112 | fontKerning: 'none', 113 | }, 114 | '.kerning-auto': { 115 | fontKerning: 'auto', 116 | }, 117 | } : {}; 118 | 119 | const textUnsetUtilities = options.textUnset ? { 120 | '.font-family-unset': { 121 | fontFamily: 'inherit', 122 | }, 123 | '.font-weight-unset': { 124 | fontWeight: 'inherit', 125 | }, 126 | '.font-style-unset': { 127 | fontStyle: 'inherit', 128 | }, 129 | '.text-size-unset': { 130 | fontSize: 'inherit', 131 | }, 132 | '.text-align-unset': { 133 | textAlign: 'inherit', 134 | }, 135 | '.leading-unset': { 136 | lineHeight: 'inherit', 137 | }, 138 | '.tracking-unset': { 139 | letterSpacing: 'inherit', 140 | }, 141 | '.text-color-unset': { 142 | color: 'inherit', 143 | }, 144 | '.text-transform-unset': { 145 | textTransform: 'inherit', 146 | }, 147 | } : {}; 148 | 149 | const fontVariantCapsUtilities = _.fromPairs( 150 | _.map(theme('fontVariantCaps'), (value, modifier) => { 151 | return [ 152 | `.${e(`caps-${modifier}`)}`, 153 | { 154 | fontVariantCaps: value, 155 | }, 156 | ]; 157 | }) 158 | ); 159 | 160 | const fontVariantNumericUtilities = _.fromPairs( 161 | _.map(theme('fontVariantNumeric'), (value, modifier) => { 162 | return [ 163 | `.${e(`nums-${modifier}`)}`, 164 | { 165 | fontVariantNumeric: value, 166 | }, 167 | ]; 168 | }) 169 | ); 170 | 171 | const fontVariantLigaturesUtilities = _.fromPairs( 172 | _.map(theme('fontVariantLigatures'), (value, modifier) => { 173 | return [ 174 | `.${e(`ligatures-${modifier}`)}`, 175 | { 176 | fontVariantLigatures: value, 177 | }, 178 | ]; 179 | }) 180 | ); 181 | 182 | const textRenderingUtilities = _.fromPairs( 183 | _.map(theme('textRendering'), (value, modifier) => { 184 | return [ 185 | `.${e(`text-${modifier}`)}`, 186 | { 187 | textRendering: value, 188 | }, 189 | ]; 190 | }) 191 | ); 192 | 193 | const textStylesTheme = theme('textStyles'); 194 | 195 | const resolveTextStyle = function(name, styles, topLevel = false) { 196 | if (_.isPlainObject(styles)) { 197 | const resolvedStyles = _.reduce(styles, function(result, value, key) { 198 | if (key === 'extends') { 199 | _.forEach(_.castArray(value), function(textStyleToExtend) { 200 | _.forEach(resolveTextStyle(textStyleToExtend, textStylesTheme[textStyleToExtend], true), function(extendedValue, extendedKey) { 201 | if (extendedKey === 'output') { 202 | return; // continue 203 | } 204 | result = { 205 | ...result, 206 | ...resolveTextStyle(extendedKey, extendedValue), 207 | }; 208 | }); 209 | }); 210 | return result; 211 | } 212 | return { 213 | ...result, 214 | ...resolveTextStyle(key, value), 215 | }; 216 | }, {}); 217 | 218 | if (topLevel) { 219 | return resolvedStyles; 220 | } 221 | 222 | return { 223 | [name]: resolvedStyles, 224 | }; 225 | } 226 | 227 | if (_.isArray(styles)) { 228 | if (name === 'fontSize' && styles.length === 2) { 229 | return { 230 | fontSize: styles[0], 231 | lineHeight: styles[1], 232 | }; 233 | } 234 | return { 235 | [name]: styles.join(', '), 236 | }; 237 | } 238 | 239 | return { 240 | [name]: styles, 241 | }; 242 | }; 243 | 244 | const textStyles = _.fromPairs( 245 | _.map(textStylesTheme, (componentStyles, componentName) => { 246 | componentStyles = resolveTextStyle(componentName, componentStyles, true); 247 | if (componentStyles.output === false) { 248 | return []; 249 | } 250 | return [ 251 | `.${e(`${options.componentPrefix}${camelCaseToKebabCase(componentName)}`)}`, 252 | componentStyles, 253 | ]; 254 | }) 255 | ); 256 | 257 | addUtilities(textIndentUtilities, variants('textIndent')); 258 | addUtilities(textShadowUtilities, variants('textShadow')); 259 | addUtilities(textDecorationStyleUtilities, variants('textDecorationStyle')); 260 | addUtilities(textDecorationColorUtilities, variants('textDecorationColor')); 261 | addUtilities(ellipsisUtilities, variants('ellipsis')); 262 | addUtilities(hyphensUtilities, variants('hyphens')); 263 | addUtilities(kerningUtilities, variants('kerning')); 264 | addUtilities(textUnsetUtilities, variants('textUnset')); 265 | addUtilities(fontVariantCapsUtilities, variants('fontVariantCaps')); 266 | addUtilities(fontVariantNumericUtilities, variants('fontVariantNumeric')); 267 | addUtilities(fontVariantLigaturesUtilities, variants('fontVariantLigatures')); 268 | addUtilities(textRenderingUtilities, variants('textRendering')); 269 | addComponents(textStyles); 270 | }; 271 | }, function() { 272 | return { 273 | theme: { 274 | textIndent: {}, 275 | textShadow: {}, 276 | textDecorationStyle: { 277 | 'solid': 'solid', 278 | 'double': 'double', 279 | 'dotted': 'dotted', 280 | 'dashed': 'dashed', 281 | 'wavy': 'wavy', 282 | }, 283 | textDecorationColor: theme => theme('colors'), 284 | fontVariantCaps: { 285 | 'normal': 'normal', 286 | 'small': 'small-caps', 287 | 'all-small': 'all-small-caps', 288 | 'petite': 'petite-caps', 289 | 'unicase': 'unicase', 290 | 'titling': 'titling-caps', 291 | }, 292 | fontVariantNumeric: { 293 | 'normal': 'normal', 294 | 'ordinal': 'ordinal', 295 | 'slashed-zero': 'slashed-zero', 296 | 'lining': 'lining-nums', 297 | 'oldstyle': 'oldstyle-nums', 298 | 'proportional': 'proportional-nums', 299 | 'tabular': 'tabular-nums', 300 | 'diagonal-fractions': 'diagonal-fractions', 301 | 'stacked-fractions': 'stacked-fractions', 302 | }, 303 | fontVariantLigatures: { 304 | 'normal': 'normal', 305 | 'none': 'none', 306 | 'common': 'common-ligatures', 307 | 'no-common': 'no-common-ligatures', 308 | 'discretionary': 'discretionary-ligatures', 309 | 'no-discretionary': 'no-discretionary-ligatures', 310 | 'historical': 'historical-ligatures', 311 | 'no-historical': 'no-historical-ligatures', 312 | 'contextual': 'contextual', 313 | 'no-contextual': 'no-contextual', 314 | }, 315 | textRendering: { 316 | 'rendering-auto': 'auto', 317 | 'optimize-legibility': 'optimizeLegibility', 318 | 'optimize-speed': 'optimizeSpeed', 319 | 'geometric-precision': 'geometricPrecision' 320 | }, 321 | textStyles: {}, 322 | }, 323 | variants: { 324 | textIndent: ['responsive'], 325 | textShadow: ['responsive'], 326 | textDecorationStyle: ['responsive'], 327 | textDecorationColor: ['responsive'], 328 | ellipsis: ['responsive'], 329 | hyphens: ['responsive'], 330 | kerning: ['responsive'], 331 | textUnset: ['responsive'], 332 | fontVariantCaps: ['responsive'], 333 | fontVariantNumeric: ['responsive'], 334 | fontVariantLigatures: ['responsive'], 335 | textRendering: ['responsive'], 336 | }, 337 | }; 338 | }); 339 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tailwindcss-typography", 3 | "version": "3.1.0", 4 | "description": "Tailwind CSS plugin to generate typography utilities and text style components", 5 | "author": "Benoît Rouleau ", 6 | "license": "ISC", 7 | "repository": "https://github.com/benface/tailwindcss-typography.git", 8 | "bugs": "https://github.com/benface/tailwindcss-typography/issues", 9 | "homepage": "https://github.com/benface/tailwindcss-typography", 10 | "scripts": { 11 | "test": "jest", 12 | "release": "f(){ release-it $1 && github-release-from-changelog ;};f" 13 | }, 14 | "dependencies": { 15 | "lodash": "^4.17.19" 16 | }, 17 | "devDependencies": { 18 | "github-release-from-changelog": "^2.1.1", 19 | "jest": "^26.1.0", 20 | "jest-matcher-css": "^1.1.0", 21 | "postcss": "^7.0.32", 22 | "release-it": "^13.6.5", 23 | "tailwindcss": "1.5.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const cssMatcher = require('jest-matcher-css'); 3 | const postcss = require('postcss'); 4 | const tailwindcss = require('tailwindcss'); 5 | const typographyPlugin = require('./index.js'); 6 | 7 | const generatePluginCss = (config, pluginOptions = {}) => { 8 | return postcss( 9 | tailwindcss( 10 | _.merge({ 11 | theme: { 12 | screens: { 13 | 'sm': '640px', 14 | }, 15 | colors: {}, 16 | }, 17 | corePlugins: false, 18 | plugins: [ 19 | typographyPlugin(pluginOptions), 20 | ], 21 | }, config) 22 | ) 23 | ) 24 | .process('@tailwind components; @tailwind utilities', { 25 | from: undefined, 26 | }) 27 | .then(result => { 28 | return result.css; 29 | }); 30 | }; 31 | 32 | expect.extend({ 33 | toMatchCss: cssMatcher, 34 | }); 35 | 36 | test('the plugin generates some utilities and responsive variants by default', () => { 37 | return generatePluginCss().then(css => { 38 | expect(css).toMatchCss(` 39 | .line-solid { 40 | text-decoration-style: solid; 41 | } 42 | .line-double { 43 | text-decoration-style: double; 44 | } 45 | .line-dotted { 46 | text-decoration-style: dotted; 47 | } 48 | .line-dashed { 49 | text-decoration-style: dashed; 50 | } 51 | .line-wavy { 52 | text-decoration-style: wavy; 53 | } 54 | .ellipsis { 55 | text-overflow: ellipsis; 56 | } 57 | .no-ellipsis { 58 | text-overflow: clip; 59 | } 60 | .hyphens-none { 61 | hyphens: none; 62 | } 63 | .hyphens-manual { 64 | hyphens: manual; 65 | } 66 | .hyphens-auto { 67 | hyphens: auto; 68 | } 69 | .kerning { 70 | font-kerning: normal; 71 | } 72 | .kerning-none { 73 | font-kerning: none; 74 | } 75 | .kerning-auto { 76 | font-kerning: auto; 77 | } 78 | .font-family-unset { 79 | font-family: inherit; 80 | } 81 | .font-weight-unset { 82 | font-weight: inherit; 83 | } 84 | .font-style-unset { 85 | font-style: inherit; 86 | } 87 | .text-size-unset { 88 | font-size: inherit; 89 | } 90 | .text-align-unset { 91 | text-align: inherit; 92 | } 93 | .leading-unset { 94 | line-height: inherit; 95 | } 96 | .tracking-unset { 97 | letter-spacing: inherit; 98 | } 99 | .text-color-unset { 100 | color: inherit; 101 | } 102 | .text-transform-unset { 103 | text-transform: inherit; 104 | } 105 | .caps-normal { 106 | font-variant-caps: normal; 107 | } 108 | .caps-small { 109 | font-variant-caps: small-caps; 110 | } 111 | .caps-all-small { 112 | font-variant-caps: all-small-caps; 113 | } 114 | .caps-petite { 115 | font-variant-caps: petite-caps; 116 | } 117 | .caps-unicase { 118 | font-variant-caps: unicase; 119 | } 120 | .caps-titling { 121 | font-variant-caps: titling-caps; 122 | } 123 | .nums-normal { 124 | font-variant-numeric: normal; 125 | } 126 | .nums-ordinal { 127 | font-variant-numeric: ordinal; 128 | } 129 | .nums-slashed-zero { 130 | font-variant-numeric: slashed-zero; 131 | } 132 | .nums-lining { 133 | font-variant-numeric: lining-nums; 134 | } 135 | .nums-oldstyle { 136 | font-variant-numeric: oldstyle-nums; 137 | } 138 | .nums-proportional { 139 | font-variant-numeric: proportional-nums; 140 | } 141 | .nums-tabular { 142 | font-variant-numeric: tabular-nums; 143 | } 144 | .nums-diagonal-fractions { 145 | font-variant-numeric: diagonal-fractions; 146 | } 147 | .nums-stacked-fractions { 148 | font-variant-numeric: stacked-fractions; 149 | } 150 | .ligatures-normal { 151 | font-variant-ligatures: normal; 152 | } 153 | .ligatures-none { 154 | font-variant-ligatures: none; 155 | } 156 | .ligatures-common { 157 | font-variant-ligatures: common-ligatures; 158 | } 159 | .ligatures-no-common { 160 | font-variant-ligatures: no-common-ligatures; 161 | } 162 | .ligatures-discretionary { 163 | font-variant-ligatures: discretionary-ligatures; 164 | } 165 | .ligatures-no-discretionary { 166 | font-variant-ligatures: no-discretionary-ligatures; 167 | } 168 | .ligatures-historical { 169 | font-variant-ligatures: historical-ligatures; 170 | } 171 | .ligatures-no-historical { 172 | font-variant-ligatures: no-historical-ligatures; 173 | } 174 | .ligatures-contextual { 175 | font-variant-ligatures: contextual; 176 | } 177 | .ligatures-no-contextual { 178 | font-variant-ligatures: no-contextual; 179 | } 180 | .text-rendering-auto { 181 | text-rendering: auto; 182 | } 183 | .text-optimize-legibility { 184 | text-rendering: optimizeLegibility; 185 | } 186 | .text-optimize-speed { 187 | text-rendering: optimizeSpeed; 188 | } 189 | .text-geometric-precision { 190 | text-rendering: geometricPrecision; 191 | } 192 | @media (min-width: 640px) { 193 | .sm\\:line-solid { 194 | text-decoration-style: solid; 195 | } 196 | .sm\\:line-double { 197 | text-decoration-style: double; 198 | } 199 | .sm\\:line-dotted { 200 | text-decoration-style: dotted; 201 | } 202 | .sm\\:line-dashed { 203 | text-decoration-style: dashed; 204 | } 205 | .sm\\:line-wavy { 206 | text-decoration-style: wavy; 207 | } 208 | .sm\\:ellipsis { 209 | text-overflow: ellipsis; 210 | } 211 | .sm\\:no-ellipsis { 212 | text-overflow: clip; 213 | } 214 | .sm\\:hyphens-none { 215 | hyphens: none; 216 | } 217 | .sm\\:hyphens-manual { 218 | hyphens: manual; 219 | } 220 | .sm\\:hyphens-auto { 221 | hyphens: auto; 222 | } 223 | .sm\\:kerning { 224 | font-kerning: normal; 225 | } 226 | .sm\\:kerning-none { 227 | font-kerning: none; 228 | } 229 | .sm\\:kerning-auto { 230 | font-kerning: auto; 231 | } 232 | .sm\\:font-family-unset { 233 | font-family: inherit; 234 | } 235 | .sm\\:font-weight-unset { 236 | font-weight: inherit; 237 | } 238 | .sm\\:font-style-unset { 239 | font-style: inherit; 240 | } 241 | .sm\\:text-size-unset { 242 | font-size: inherit; 243 | } 244 | .sm\\:text-align-unset { 245 | text-align: inherit; 246 | } 247 | .sm\\:leading-unset { 248 | line-height: inherit; 249 | } 250 | .sm\\:tracking-unset { 251 | letter-spacing: inherit; 252 | } 253 | .sm\\:text-color-unset { 254 | color: inherit; 255 | } 256 | .sm\\:text-transform-unset { 257 | text-transform: inherit; 258 | } 259 | .sm\\:caps-normal { 260 | font-variant-caps: normal; 261 | } 262 | .sm\\:caps-small { 263 | font-variant-caps: small-caps; 264 | } 265 | .sm\\:caps-all-small { 266 | font-variant-caps: all-small-caps; 267 | } 268 | .sm\\:caps-petite { 269 | font-variant-caps: petite-caps; 270 | } 271 | .sm\\:caps-unicase { 272 | font-variant-caps: unicase; 273 | } 274 | .sm\\:caps-titling { 275 | font-variant-caps: titling-caps; 276 | } 277 | .sm\\:nums-normal { 278 | font-variant-numeric: normal; 279 | } 280 | .sm\\:nums-ordinal { 281 | font-variant-numeric: ordinal; 282 | } 283 | .sm\\:nums-slashed-zero { 284 | font-variant-numeric: slashed-zero; 285 | } 286 | .sm\\:nums-lining { 287 | font-variant-numeric: lining-nums; 288 | } 289 | .sm\\:nums-oldstyle { 290 | font-variant-numeric: oldstyle-nums; 291 | } 292 | .sm\\:nums-proportional { 293 | font-variant-numeric: proportional-nums; 294 | } 295 | .sm\\:nums-tabular { 296 | font-variant-numeric: tabular-nums; 297 | } 298 | .sm\\:nums-diagonal-fractions { 299 | font-variant-numeric: diagonal-fractions; 300 | } 301 | .sm\\:nums-stacked-fractions { 302 | font-variant-numeric: stacked-fractions; 303 | } 304 | .sm\\:ligatures-normal { 305 | font-variant-ligatures: normal; 306 | } 307 | .sm\\:ligatures-none { 308 | font-variant-ligatures: none; 309 | } 310 | .sm\\:ligatures-common { 311 | font-variant-ligatures: common-ligatures; 312 | } 313 | .sm\\:ligatures-no-common { 314 | font-variant-ligatures: no-common-ligatures; 315 | } 316 | .sm\\:ligatures-discretionary { 317 | font-variant-ligatures: discretionary-ligatures; 318 | } 319 | .sm\\:ligatures-no-discretionary { 320 | font-variant-ligatures: no-discretionary-ligatures; 321 | } 322 | .sm\\:ligatures-historical { 323 | font-variant-ligatures: historical-ligatures; 324 | } 325 | .sm\\:ligatures-no-historical { 326 | font-variant-ligatures: no-historical-ligatures; 327 | } 328 | .sm\\:ligatures-contextual { 329 | font-variant-ligatures: contextual; 330 | } 331 | .sm\\:ligatures-no-contextual { 332 | font-variant-ligatures: no-contextual; 333 | } 334 | .sm\\:text-rendering-auto { 335 | text-rendering: auto; 336 | } 337 | .sm\\:text-optimize-legibility { 338 | text-rendering: optimizeLegibility; 339 | } 340 | .sm\\:text-optimize-speed { 341 | text-rendering: optimizeSpeed; 342 | } 343 | .sm\\:text-geometric-precision { 344 | text-rendering: geometricPrecision; 345 | } 346 | } 347 | `); 348 | }); 349 | }); 350 | 351 | test('the font variant utilities can be disabled', () => { 352 | return generatePluginCss({ 353 | theme: { 354 | fontVariantCaps: {}, 355 | fontVariantNumeric: {}, 356 | fontVariantLigatures: {}, 357 | textRendering: {}, 358 | }, 359 | variants: { 360 | textDecorationStyle: [], 361 | textDecorationColor: [], 362 | ellipsis: [], 363 | hyphens: [], 364 | kerning: [], 365 | textUnset: [], 366 | }, 367 | }).then(css => { 368 | expect(css).toMatchCss(` 369 | .line-solid { 370 | text-decoration-style: solid; 371 | } 372 | .line-double { 373 | text-decoration-style: double; 374 | } 375 | .line-dotted { 376 | text-decoration-style: dotted; 377 | } 378 | .line-dashed { 379 | text-decoration-style: dashed; 380 | } 381 | .line-wavy { 382 | text-decoration-style: wavy; 383 | } 384 | .ellipsis { 385 | text-overflow: ellipsis; 386 | } 387 | .no-ellipsis { 388 | text-overflow: clip; 389 | } 390 | .hyphens-none { 391 | hyphens: none; 392 | } 393 | .hyphens-manual { 394 | hyphens: manual; 395 | } 396 | .hyphens-auto { 397 | hyphens: auto; 398 | } 399 | .kerning { 400 | font-kerning: normal; 401 | } 402 | .kerning-none { 403 | font-kerning: none; 404 | } 405 | .kerning-auto { 406 | font-kerning: auto; 407 | } 408 | .font-family-unset { 409 | font-family: inherit; 410 | } 411 | .font-weight-unset { 412 | font-weight: inherit; 413 | } 414 | .font-style-unset { 415 | font-style: inherit; 416 | } 417 | .text-size-unset { 418 | font-size: inherit; 419 | } 420 | .text-align-unset { 421 | text-align: inherit; 422 | } 423 | .leading-unset { 424 | line-height: inherit; 425 | } 426 | .tracking-unset { 427 | letter-spacing: inherit; 428 | } 429 | .text-color-unset { 430 | color: inherit; 431 | } 432 | .text-transform-unset { 433 | text-transform: inherit; 434 | } 435 | @media (min-width: 640px) { 436 | } 437 | `); 438 | }); 439 | }); 440 | 441 | test('the ellipsis, hyphens, and text unset utilities can be disabled', () => { 442 | return generatePluginCss({ 443 | theme: { 444 | textDecorationStyle: {}, 445 | textDecorationColor: {}, 446 | fontVariantCaps: {}, 447 | fontVariantNumeric: {}, 448 | fontVariantLigatures: {}, 449 | textRendering: {}, 450 | }, 451 | }, { 452 | ellipsis: false, 453 | hyphens: false, 454 | kerning: false, 455 | textUnset: false, 456 | }).then(css => { 457 | expect(css).toMatchCss(` 458 | @media (min-width: 640px) { 459 | } 460 | `); 461 | }); 462 | }); 463 | 464 | test('the text decoration color utilities default to the theme’s colors', () => { 465 | return generatePluginCss({ 466 | theme: { 467 | colors: { 468 | 'yellow': '#ff0', 469 | 'white': '#fff', 470 | }, 471 | textDecorationStyle: {}, 472 | fontVariantCaps: {}, 473 | fontVariantNumeric: {}, 474 | fontVariantLigatures: {}, 475 | textRendering: {}, 476 | }, 477 | variants: { 478 | textDecorationStyle: [], 479 | textDecorationColor: [], 480 | }, 481 | }, { 482 | ellipsis: false, 483 | hyphens: false, 484 | kerning: false, 485 | textUnset: false, 486 | }).then(css => { 487 | expect(css).toMatchCss(` 488 | .line-yellow { 489 | text-decoration-color: #ff0; 490 | } 491 | .line-white { 492 | text-decoration-color: #fff; 493 | } 494 | @media (min-width: 640px) { 495 | } 496 | `); 497 | }); 498 | }); 499 | 500 | test('the text indent, text shadow, and text decoration utilities can be customized', () => { 501 | return generatePluginCss({ 502 | theme: { 503 | textIndent: { 504 | '1': '0.25rem', 505 | '2': '0.5rem', 506 | '-1': '-0.25rem', 507 | }, 508 | textShadow: { 509 | 'default': '0 2px 5px rgba(0, 0, 0, 0.5)', 510 | 'lg': '0 2px 10px rgba(0, 0, 0, 0.5)', 511 | }, 512 | textDecorationStyle: { 513 | 'groovy': 'wavy', 514 | }, 515 | textDecorationColor: { 516 | 'red': '#f00', 517 | 'green': '#0f0', 518 | 'blue': '#00f', 519 | }, 520 | fontVariantCaps: {}, 521 | fontVariantNumeric: {}, 522 | fontVariantLigatures: {}, 523 | textRendering: {}, 524 | }, 525 | variants: { 526 | textIndent: [], 527 | textShadow: [], 528 | textDecorationStyle: [], 529 | textDecorationColor: [], 530 | }, 531 | }, { 532 | ellipsis: false, 533 | hyphens: false, 534 | kerning: false, 535 | textUnset: false, 536 | }).then(css => { 537 | expect(css).toMatchCss(` 538 | .indent-1 { 539 | text-indent: 0.25rem; 540 | } 541 | .indent-2 { 542 | text-indent: 0.5rem; 543 | } 544 | .-indent-1 { 545 | text-indent: -0.25rem; 546 | } 547 | .text-shadow { 548 | text-shadow: 0 2px 5px rgba(0, 0, 0, 0.5); 549 | } 550 | .text-shadow-lg { 551 | text-shadow: 0 2px 10px rgba(0, 0, 0, 0.5); 552 | } 553 | .line-groovy { 554 | text-decoration-style: wavy; 555 | } 556 | .line-red { 557 | text-decoration-color: #f00; 558 | } 559 | .line-green { 560 | text-decoration-color: #0f0; 561 | } 562 | .line-blue { 563 | text-decoration-color: #00f; 564 | } 565 | @media (min-width: 640px) { 566 | } 567 | `); 568 | }); 569 | }); 570 | 571 | test('the text decoration color utilities can use nested object notation', () => { 572 | return generatePluginCss({ 573 | theme: { 574 | colors: { 575 | indigo: { 576 | lighter: '#b3bcf5', 577 | default: '#5c6ac4', 578 | dark: '#202e78', 579 | }, 580 | }, 581 | textDecorationStyle: {}, 582 | textDecorationColor: theme => theme('colors'), 583 | fontVariantCaps: {}, 584 | fontVariantNumeric: {}, 585 | fontVariantLigatures: {}, 586 | textRendering: {}, 587 | }, 588 | variants: { 589 | textDecorationStyle: [], 590 | textDecorationColor: [], 591 | }, 592 | }, { 593 | ellipsis: false, 594 | hyphens: false, 595 | kerning: false, 596 | textUnset: false, 597 | }).then(css => { 598 | expect(css).toMatchCss(` 599 | .line-indigo-lighter { 600 | text-decoration-color: #b3bcf5; 601 | } 602 | .line-indigo { 603 | text-decoration-color: #5c6ac4; 604 | } 605 | .line-indigo-dark { 606 | text-decoration-color: #202e78; 607 | } 608 | @media (min-width: 640px) { 609 | } 610 | `); 611 | }); 612 | }); 613 | 614 | test('the font variant utilities can be extended', () => { 615 | return generatePluginCss({ 616 | theme: { 617 | textDecorationStyle: {}, 618 | textDecorationColor: {}, 619 | extend: { 620 | fontVariantCaps: { 621 | 'inherit': 'inherit', 622 | }, 623 | fontVariantNumeric: { 624 | 'initial': 'initial', 625 | }, 626 | fontVariantLigatures: { 627 | 'revert': 'revert', 628 | }, 629 | }, 630 | }, 631 | variants: { 632 | fontVariantCaps: [], 633 | fontVariantNumeric: [], 634 | fontVariantLigatures: [], 635 | textRendering: [], 636 | }, 637 | }, { 638 | ellipsis: false, 639 | hyphens: false, 640 | kerning: false, 641 | textUnset: false, 642 | }).then(css => { 643 | expect(css).toMatchCss(` 644 | .caps-normal { 645 | font-variant-caps: normal; 646 | } 647 | .caps-small { 648 | font-variant-caps: small-caps; 649 | } 650 | .caps-all-small { 651 | font-variant-caps: all-small-caps; 652 | } 653 | .caps-petite { 654 | font-variant-caps: petite-caps; 655 | } 656 | .caps-unicase { 657 | font-variant-caps: unicase; 658 | } 659 | .caps-titling { 660 | font-variant-caps: titling-caps; 661 | } 662 | .caps-inherit { 663 | font-variant-caps: inherit; 664 | } 665 | .nums-normal { 666 | font-variant-numeric: normal; 667 | } 668 | .nums-ordinal { 669 | font-variant-numeric: ordinal; 670 | } 671 | .nums-slashed-zero { 672 | font-variant-numeric: slashed-zero; 673 | } 674 | .nums-lining { 675 | font-variant-numeric: lining-nums; 676 | } 677 | .nums-oldstyle { 678 | font-variant-numeric: oldstyle-nums; 679 | } 680 | .nums-proportional { 681 | font-variant-numeric: proportional-nums; 682 | } 683 | .nums-tabular { 684 | font-variant-numeric: tabular-nums; 685 | } 686 | .nums-diagonal-fractions { 687 | font-variant-numeric: diagonal-fractions; 688 | } 689 | .nums-stacked-fractions { 690 | font-variant-numeric: stacked-fractions; 691 | } 692 | .nums-initial { 693 | font-variant-numeric: initial; 694 | } 695 | .ligatures-normal { 696 | font-variant-ligatures: normal; 697 | } 698 | .ligatures-none { 699 | font-variant-ligatures: none; 700 | } 701 | .ligatures-common { 702 | font-variant-ligatures: common-ligatures; 703 | } 704 | .ligatures-no-common { 705 | font-variant-ligatures: no-common-ligatures; 706 | } 707 | .ligatures-discretionary { 708 | font-variant-ligatures: discretionary-ligatures; 709 | } 710 | .ligatures-no-discretionary { 711 | font-variant-ligatures: no-discretionary-ligatures; 712 | } 713 | .ligatures-historical { 714 | font-variant-ligatures: historical-ligatures; 715 | } 716 | .ligatures-no-historical { 717 | font-variant-ligatures: no-historical-ligatures; 718 | } 719 | .ligatures-contextual { 720 | font-variant-ligatures: contextual; 721 | } 722 | .ligatures-no-contextual { 723 | font-variant-ligatures: no-contextual; 724 | } 725 | .ligatures-revert { 726 | font-variant-ligatures: revert; 727 | } 728 | .text-rendering-auto { 729 | text-rendering: auto; 730 | } 731 | .text-optimize-legibility { 732 | text-rendering: optimizeLegibility; 733 | } 734 | .text-optimize-speed { 735 | text-rendering: optimizeSpeed; 736 | } 737 | .text-geometric-precision { 738 | text-rendering: geometricPrecision; 739 | } 740 | @media (min-width: 640px) { 741 | } 742 | `); 743 | }); 744 | }); 745 | 746 | test('text style components can be generated', () => { 747 | return generatePluginCss({ 748 | theme: { 749 | fontSize: { 750 | 'heading-xs': '24px', 751 | 'heading-sm': '30px', 752 | 'heading': '36px', 753 | 'heading-lg': '48px', 754 | 'heading-xl': '64px', 755 | }, 756 | textStyles: theme => ({ 757 | h1: { 758 | fontSize: theme('fontSize.heading-xl'), 759 | }, 760 | h2: { 761 | fontSize: theme('fontSize.heading-lg'), 762 | }, 763 | h3: { 764 | fontSize: theme('fontSize.heading'), 765 | }, 766 | h4: { 767 | fontSize: theme('fontSize.heading-sm'), 768 | }, 769 | h5: { 770 | fontSize: theme('fontSize.heading-xs'), 771 | }, 772 | }), 773 | textDecorationStyle: {}, 774 | textDecorationColor: {}, 775 | fontVariantCaps: {}, 776 | fontVariantNumeric: {}, 777 | fontVariantLigatures: {}, 778 | textRendering: {}, 779 | }, 780 | }, { 781 | ellipsis: false, 782 | hyphens: false, 783 | kerning: false, 784 | textUnset: false, 785 | }).then(css => { 786 | expect(css).toMatchCss(` 787 | .c-h1 { 788 | font-size: 64px; 789 | } 790 | .c-h2 { 791 | font-size: 48px; 792 | } 793 | .c-h3 { 794 | font-size: 36px; 795 | } 796 | .c-h4 { 797 | font-size: 30px; 798 | } 799 | .c-h5 { 800 | font-size: 24px; 801 | } 802 | @media (min-width: 640px) { 803 | } 804 | `); 805 | }); 806 | }); 807 | 808 | test('the component prefix can be customized', () => { 809 | return generatePluginCss({ 810 | theme: { 811 | fontSize: { 812 | 'heading-xl': '64px', 813 | }, 814 | textStyles: theme => ({ 815 | h1: { 816 | fontSize: theme('fontSize.heading-xl'), 817 | }, 818 | }), 819 | textDecorationStyle: {}, 820 | textDecorationColor: {}, 821 | fontVariantCaps: {}, 822 | fontVariantNumeric: {}, 823 | fontVariantLigatures: {}, 824 | textRendering: {}, 825 | }, 826 | }, { 827 | ellipsis: false, 828 | hyphens: false, 829 | kerning: false, 830 | textUnset: false, 831 | componentPrefix: '', 832 | }).then(css => { 833 | expect(css).toMatchCss(` 834 | .h1 { 835 | font-size: 64px; 836 | } 837 | @media (min-width: 640px) { 838 | } 839 | `); 840 | }); 841 | }); 842 | 843 | test('text styles can extend other text styles', () => { 844 | return generatePluginCss({ 845 | theme: { 846 | fontWeight: { 847 | 'bold': '700', 848 | }, 849 | fontSize: { 850 | 'heading-xs': '24px', 851 | 'heading-sm': '30px', 852 | 'heading': '36px', 853 | 'heading-lg': '48px', 854 | 'heading-xl': '64px', 855 | }, 856 | lineHeight: { 857 | 'none': '1', 858 | 'tight': '1.25', 859 | 'normal': '1.5', 860 | }, 861 | textStyles: theme => ({ 862 | heading: { 863 | fontWeight: theme('fontWeight.bold'), 864 | lineHeight: theme('lineHeight.tight'), 865 | }, 866 | h1: { 867 | extends: 'heading', 868 | fontSize: theme('fontSize.heading-xl'), 869 | }, 870 | h2: { 871 | extends: 'heading', 872 | fontSize: theme('fontSize.heading-lg'), 873 | }, 874 | h3: { 875 | extends: 'heading', 876 | fontSize: theme('fontSize.heading'), 877 | }, 878 | }), 879 | textDecorationStyle: {}, 880 | textDecorationColor: {}, 881 | fontVariantCaps: {}, 882 | fontVariantNumeric: {}, 883 | fontVariantLigatures: {}, 884 | textRendering: {}, 885 | }, 886 | }, { 887 | ellipsis: false, 888 | hyphens: false, 889 | kerning: false, 890 | textUnset: false, 891 | }).then(css => { 892 | expect(css).toMatchCss(` 893 | .c-heading { 894 | font-weight: 700; 895 | line-height: 1.25; 896 | } 897 | .c-h1 { 898 | font-weight: 700; 899 | line-height: 1.25; 900 | font-size: 64px; 901 | } 902 | .c-h2 { 903 | font-weight: 700; 904 | line-height: 1.25; 905 | font-size: 48px; 906 | } 907 | .c-h3 { 908 | font-weight: 700; 909 | line-height: 1.25; 910 | font-size: 36px; 911 | } 912 | @media (min-width: 640px) { 913 | } 914 | `); 915 | }); 916 | }); 917 | 918 | test('text styles can extend more than one other text style', () => { 919 | return generatePluginCss({ 920 | theme: { 921 | fontFamily: { 922 | 'default': 'sans-serif', 923 | 'heading': 'Helvetica', 924 | }, 925 | fontWeight: { 926 | 'bold': '700', 927 | 'extrabold': '800', 928 | }, 929 | fontSize: { 930 | 'heading-xs': '24px', 931 | 'heading-sm': '30px', 932 | 'heading': '36px', 933 | 'heading-lg': '48px', 934 | 'heading-xl': '64px', 935 | }, 936 | lineHeight: { 937 | 'none': '1', 938 | 'tight': '1.25', 939 | 'normal': '1.5', 940 | }, 941 | textStyles: theme => ({ 942 | heading: { 943 | fontFamily: theme('fontFamily.heading'), 944 | fontWeight: theme('fontWeight.bold'), 945 | }, 946 | largeHeading: { 947 | fontWeight: theme('fontWeight.extrabold'), 948 | lineHeight: theme('lineHeight.tight'), 949 | }, 950 | h1: { 951 | extends: ['heading', 'largeHeading'], 952 | fontSize: theme('fontSize.heading-xl'), 953 | }, 954 | h2: { 955 | extends: ['heading', 'largeHeading'], 956 | fontSize: theme('fontSize.heading-lg'), 957 | }, 958 | h3: { 959 | extends: 'heading', 960 | fontSize: theme('fontSize.heading'), 961 | }, 962 | }), 963 | textDecorationStyle: {}, 964 | textDecorationColor: {}, 965 | fontVariantCaps: {}, 966 | fontVariantNumeric: {}, 967 | fontVariantLigatures: {}, 968 | textRendering: {}, 969 | }, 970 | }, { 971 | ellipsis: false, 972 | hyphens: false, 973 | kerning: false, 974 | textUnset: false, 975 | }).then(css => { 976 | expect(css).toMatchCss(` 977 | .c-heading { 978 | font-family: Helvetica; 979 | font-weight: 700; 980 | } 981 | .c-large-heading { 982 | font-weight: 800; 983 | line-height: 1.25; 984 | } 985 | .c-h1 { 986 | font-family: Helvetica; 987 | font-weight: 800; 988 | line-height: 1.25; 989 | font-size: 64px; 990 | } 991 | .c-h2 { 992 | font-family: Helvetica; 993 | font-weight: 800; 994 | line-height: 1.25; 995 | font-size: 48px; 996 | } 997 | .c-h3 { 998 | font-family: Helvetica; 999 | font-weight: 700; 1000 | font-size: 36px; 1001 | } 1002 | @media (min-width: 640px) { 1003 | } 1004 | `); 1005 | }); 1006 | }); 1007 | 1008 | test('text styles support arrays in fontFamily and fontSize properties', () => { 1009 | return generatePluginCss({ 1010 | theme: { 1011 | fontFamily: { 1012 | 'default': 'sans-serif', 1013 | 'heading': ['Helvetica', 'Arial', 'sans-serif'], 1014 | }, 1015 | fontWeight: { 1016 | 'bold': '700', 1017 | 'extrabold': '800', 1018 | }, 1019 | fontSize: { 1020 | 'heading-xs': ['24px', '36px'], 1021 | 'heading-sm': ['30px', '45px'], 1022 | 'heading': ['36px', '54px'], 1023 | 'heading-lg': ['48px', '72px'], 1024 | 'heading-xl': ['64px', '96px'], 1025 | }, 1026 | lineHeight: { 1027 | 'none': '1', 1028 | 'tight': '1.25', 1029 | 'normal': '1.5', 1030 | }, 1031 | textStyles: theme => ({ 1032 | heading: { 1033 | fontFamily: theme('fontFamily.heading'), 1034 | fontWeight: theme('fontWeight.bold'), 1035 | }, 1036 | largeHeading: { 1037 | fontWeight: theme('fontWeight.extrabold'), 1038 | }, 1039 | h1: { 1040 | extends: ['heading', 'largeHeading'], 1041 | fontSize: theme('fontSize.heading-xl'), 1042 | }, 1043 | h2: { 1044 | extends: ['heading', 'largeHeading'], 1045 | fontSize: theme('fontSize.heading-lg'), 1046 | }, 1047 | h3: { 1048 | extends: 'heading', 1049 | fontSize: theme('fontSize.heading'), 1050 | }, 1051 | }), 1052 | textDecorationStyle: {}, 1053 | textDecorationColor: {}, 1054 | fontVariantCaps: {}, 1055 | fontVariantNumeric: {}, 1056 | fontVariantLigatures: {}, 1057 | textRendering: {}, 1058 | }, 1059 | }, { 1060 | ellipsis: false, 1061 | hyphens: false, 1062 | kerning: false, 1063 | textUnset: false, 1064 | }).then(css => { 1065 | expect(css).toMatchCss(` 1066 | .c-heading { 1067 | font-family: Helvetica, Arial, sans-serif; 1068 | font-weight: 700; 1069 | } 1070 | .c-large-heading { 1071 | font-weight: 800; 1072 | } 1073 | .c-h1 { 1074 | font-family: Helvetica, Arial, sans-serif; 1075 | font-weight: 800; 1076 | font-size: 64px; 1077 | line-height: 96px; 1078 | } 1079 | .c-h2 { 1080 | font-family: Helvetica, Arial, sans-serif; 1081 | font-weight: 800; 1082 | font-size: 48px; 1083 | line-height: 72px; 1084 | } 1085 | .c-h3 { 1086 | font-family: Helvetica, Arial, sans-serif; 1087 | font-weight: 700; 1088 | font-size: 36px; 1089 | line-height: 54px; 1090 | } 1091 | @media (min-width: 640px) { 1092 | } 1093 | `); 1094 | }); 1095 | }); 1096 | 1097 | test('text style components can style their children', () => { 1098 | return generatePluginCss({ 1099 | theme: { 1100 | fontWeight: { 1101 | 'normal': '400', 1102 | 'bold': '700', 1103 | }, 1104 | fontSize: { 1105 | 'sm': '14px', 1106 | 'default': '16px', 1107 | 'lg': '18px', 1108 | 'xl': '20px', 1109 | 'heading-xs': '24px', 1110 | 'heading-sm': '30px', 1111 | 'heading': '36px', 1112 | 'heading-lg': '48px', 1113 | 'heading-xl': '64px', 1114 | }, 1115 | lineHeight: { 1116 | 'none': '1', 1117 | 'tight': '1.25', 1118 | 'normal': '1.5', 1119 | 'loose': '2', 1120 | }, 1121 | textStyles: theme => ({ 1122 | heading: { 1123 | fontWeight: theme('fontWeight.bold'), 1124 | lineHeight: theme('lineHeight.tight'), 1125 | }, 1126 | h1: { 1127 | extends: 'heading', 1128 | fontSize: theme('fontSize.heading-xl'), 1129 | }, 1130 | h2: { 1131 | extends: 'heading', 1132 | fontSize: theme('fontSize.heading-lg'), 1133 | }, 1134 | h3: { 1135 | extends: 'heading', 1136 | fontSize: theme('fontSize.heading'), 1137 | }, 1138 | richText: { 1139 | fontWeight: theme('fontWeight.normal'), 1140 | fontSize: theme('fontSize.default'), 1141 | lineHeight: theme('lineHeight.loose'), 1142 | 'h1': { 1143 | extends: 'h1', 1144 | }, 1145 | 'h2': { 1146 | extends: 'h2', 1147 | }, 1148 | 'h3': { 1149 | extends: 'h3', 1150 | }, 1151 | }, 1152 | }), 1153 | textDecorationStyle: {}, 1154 | textDecorationColor: {}, 1155 | fontVariantCaps: {}, 1156 | fontVariantNumeric: {}, 1157 | fontVariantLigatures: {}, 1158 | textRendering: {}, 1159 | }, 1160 | }, { 1161 | ellipsis: false, 1162 | hyphens: false, 1163 | kerning: false, 1164 | textUnset: false, 1165 | }).then(css => { 1166 | expect(css).toMatchCss(` 1167 | .c-heading { 1168 | font-weight: 700; 1169 | line-height: 1.25; 1170 | } 1171 | .c-h1 { 1172 | font-weight: 700; 1173 | line-height: 1.25; 1174 | font-size: 64px; 1175 | } 1176 | .c-h2 { 1177 | font-weight: 700; 1178 | line-height: 1.25; 1179 | font-size: 48px; 1180 | } 1181 | .c-h3 { 1182 | font-weight: 700; 1183 | line-height: 1.25; 1184 | font-size: 36px; 1185 | } 1186 | .c-rich-text { 1187 | font-weight: 400; 1188 | font-size: 16px; 1189 | line-height: 2; 1190 | } 1191 | .c-rich-text h1 { 1192 | font-weight: 700; 1193 | line-height: 1.25; 1194 | font-size: 64px; 1195 | } 1196 | .c-rich-text h2 { 1197 | font-weight: 700; 1198 | line-height: 1.25; 1199 | font-size: 48px; 1200 | } 1201 | .c-rich-text h3 { 1202 | font-weight: 700; 1203 | line-height: 1.25; 1204 | font-size: 36px; 1205 | } 1206 | @media (min-width: 640px) { 1207 | } 1208 | `); 1209 | }); 1210 | }); 1211 | 1212 | test('text styles can be responsive', () => { 1213 | return generatePluginCss({ 1214 | theme: { 1215 | fontWeight: { 1216 | 'bold': '700', 1217 | }, 1218 | fontSize: { 1219 | 'heading-xs': '24px', 1220 | 'heading-sm': '30px', 1221 | 'heading': '36px', 1222 | 'heading-lg': '48px', 1223 | 'heading-xl': '64px', 1224 | }, 1225 | lineHeight: { 1226 | 'none': '1', 1227 | 'tight': '1.25', 1228 | 'normal': '1.5', 1229 | }, 1230 | textStyles: theme => ({ 1231 | heading: { 1232 | fontWeight: theme('fontWeight.bold'), 1233 | lineHeight: theme('lineHeight.tight'), 1234 | }, 1235 | h1: { 1236 | extends: 'heading', 1237 | fontSize: theme('fontSize.heading-lg'), 1238 | '@screen sm': { 1239 | fontSize: theme('fontSize.heading-xl'), 1240 | } 1241 | }, 1242 | h2: { 1243 | extends: 'heading', 1244 | fontSize: theme('fontSize.heading'), 1245 | '@screen sm': { 1246 | fontSize: theme('fontSize.heading-lg'), 1247 | } 1248 | }, 1249 | h3: { 1250 | extends: 'heading', 1251 | fontSize: theme('fontSize.heading-sm'), 1252 | '@screen sm': { 1253 | fontSize: theme('fontSize.heading'), 1254 | } 1255 | }, 1256 | }), 1257 | textDecorationStyle: {}, 1258 | textDecorationColor: {}, 1259 | fontVariantCaps: {}, 1260 | fontVariantNumeric: {}, 1261 | fontVariantLigatures: {}, 1262 | textRendering: {}, 1263 | }, 1264 | }, { 1265 | ellipsis: false, 1266 | hyphens: false, 1267 | kerning: false, 1268 | textUnset: false, 1269 | }).then(css => { 1270 | expect(css).toMatchCss(` 1271 | .c-heading { 1272 | font-weight: 700; 1273 | line-height: 1.25; 1274 | } 1275 | .c-h1 { 1276 | font-weight: 700; 1277 | line-height: 1.25; 1278 | font-size: 48px; 1279 | } 1280 | @media (min-width: 640px) { 1281 | .c-h1 { 1282 | font-size: 64px; 1283 | } 1284 | } 1285 | .c-h2 { 1286 | font-weight: 700; 1287 | line-height: 1.25; 1288 | font-size: 36px; 1289 | } 1290 | @media (min-width: 640px) { 1291 | .c-h2 { 1292 | font-size: 48px; 1293 | } 1294 | } 1295 | .c-h3 { 1296 | font-weight: 700; 1297 | line-height: 1.25; 1298 | font-size: 30px; 1299 | } 1300 | @media (min-width: 640px) { 1301 | .c-h3 { 1302 | font-size: 36px; 1303 | } 1304 | } 1305 | @media (min-width: 640px) { 1306 | } 1307 | `); 1308 | }); 1309 | }); 1310 | 1311 | test('text styles can be set to not be output', () => { 1312 | return generatePluginCss({ 1313 | theme: { 1314 | fontWeight: { 1315 | 'bold': '700', 1316 | }, 1317 | fontSize: { 1318 | 'heading-xs': '24px', 1319 | 'heading-sm': '30px', 1320 | 'heading': '36px', 1321 | 'heading-lg': '48px', 1322 | 'heading-xl': '64px', 1323 | }, 1324 | lineHeight: { 1325 | 'none': '1', 1326 | 'tight': '1.25', 1327 | 'normal': '1.5', 1328 | }, 1329 | textStyles: theme => ({ 1330 | heading: { 1331 | output: false, 1332 | fontWeight: theme('fontWeight.bold'), 1333 | lineHeight: theme('lineHeight.tight'), 1334 | fontSize: theme('fontSize.heading'), 1335 | }, 1336 | h1: { 1337 | extends: 'heading', 1338 | fontSize: theme('fontSize.heading-xl'), 1339 | }, 1340 | }), 1341 | textDecorationStyle: {}, 1342 | textDecorationColor: {}, 1343 | fontVariantCaps: {}, 1344 | fontVariantNumeric: {}, 1345 | fontVariantLigatures: {}, 1346 | textRendering: {}, 1347 | }, 1348 | }, { 1349 | ellipsis: false, 1350 | hyphens: false, 1351 | kerning: false, 1352 | textUnset: false, 1353 | }).then(css => { 1354 | expect(css).toMatchCss(` 1355 | .c-h1 { 1356 | font-weight: 700; 1357 | line-height: 1.25; 1358 | font-size: 64px; 1359 | } 1360 | @media (min-width: 640px) { 1361 | } 1362 | `); 1363 | }); 1364 | }); 1365 | 1366 | test('all these options can be used to generate a full-featured rich text component', () => { 1367 | return generatePluginCss({ 1368 | theme: { 1369 | colors: { 1370 | 'link': '#2083c0', 1371 | }, 1372 | fontFamily: { 1373 | 'default': 'sans-serif', 1374 | 'heading': 'Helvetica', 1375 | }, 1376 | fontWeight: { 1377 | 'normal': '400', 1378 | 'bold': '700', 1379 | }, 1380 | fontSize: { 1381 | 'sm': '14px', 1382 | 'default': '16px', 1383 | 'lg': '18px', 1384 | 'xl': '20px', 1385 | 'heading-xs': '24px', 1386 | 'heading-sm': '30px', 1387 | 'heading': '36px', 1388 | 'heading-lg': '48px', 1389 | 'heading-xl': '64px', 1390 | }, 1391 | lineHeight: { 1392 | 'none': '1', 1393 | 'tight': '1.25', 1394 | 'normal': '1.5', 1395 | 'loose': '2', 1396 | }, 1397 | textStyles: theme => ({ 1398 | heading: { 1399 | output: false, 1400 | fontFamily: theme('fontFamily.heading'), 1401 | fontWeight: theme('fontWeight.bold'), 1402 | lineHeight: theme('lineHeight.tight'), 1403 | }, 1404 | link: { 1405 | output: false, 1406 | fontWeight: theme('fontWeight.bold'), 1407 | color: theme('colors.link'), 1408 | '&:hover': { 1409 | opacity: '0.5', 1410 | textDecoration: 'underline', 1411 | }, 1412 | }, 1413 | richText: { 1414 | fontFamily: theme('fontFamily.default'), 1415 | fontWeight: theme('fontWeight.normal'), 1416 | fontSize: theme('fontSize.default'), 1417 | lineHeight: theme('lineHeight.loose'), 1418 | '> * + *': { 1419 | marginTop: '1em', 1420 | }, 1421 | 'h1': { 1422 | extends: 'heading', 1423 | fontSize: theme('fontSize.heading-xl'), 1424 | }, 1425 | 'h2': { 1426 | extends: 'heading', 1427 | fontSize: theme('fontSize.heading-lg'), 1428 | }, 1429 | 'h3': { 1430 | extends: 'heading', 1431 | fontSize: theme('fontSize.heading'), 1432 | }, 1433 | 'h4': { 1434 | extends: 'heading', 1435 | fontSize: theme('fontSize.heading-sm'), 1436 | }, 1437 | 'h5': { 1438 | extends: 'heading', 1439 | fontSize: theme('fontSize.heading-xs'), 1440 | }, 1441 | 'h6': { 1442 | extends: 'heading', 1443 | fontSize: theme('fontSize.xl'), 1444 | }, 1445 | 'ul': { 1446 | listStyleType: 'disc', 1447 | }, 1448 | 'ol': { 1449 | listStyleType: 'decimal', 1450 | }, 1451 | 'a': { 1452 | extends: 'link', 1453 | }, 1454 | 'b, strong': { 1455 | fontWeight: theme('fontWeight.bold'), 1456 | }, 1457 | 'i, em': { 1458 | fontStyle: 'italic', 1459 | }, 1460 | }, 1461 | }), 1462 | textDecorationStyle: {}, 1463 | textDecorationColor: {}, 1464 | fontVariantCaps: {}, 1465 | fontVariantNumeric: {}, 1466 | fontVariantLigatures: {}, 1467 | textRendering: {}, 1468 | }, 1469 | }, { 1470 | ellipsis: false, 1471 | hyphens: false, 1472 | kerning: false, 1473 | textUnset: false, 1474 | }).then(css => { 1475 | expect(css).toMatchCss(` 1476 | .c-rich-text { 1477 | font-family: sans-serif; 1478 | font-weight: 400; 1479 | font-size: 16px; 1480 | line-height: 2; 1481 | } 1482 | .c-rich-text > * + * { 1483 | margin-top: 1em; 1484 | } 1485 | .c-rich-text h1 { 1486 | font-family: Helvetica; 1487 | font-weight: 700; 1488 | line-height: 1.25; 1489 | font-size: 64px; 1490 | } 1491 | .c-rich-text h2 { 1492 | font-family: Helvetica; 1493 | font-weight: 700; 1494 | line-height: 1.25; 1495 | font-size: 48px; 1496 | } 1497 | .c-rich-text h3 { 1498 | font-family: Helvetica; 1499 | font-weight: 700; 1500 | line-height: 1.25; 1501 | font-size: 36px; 1502 | } 1503 | .c-rich-text h4 { 1504 | font-family: Helvetica; 1505 | font-weight: 700; 1506 | line-height: 1.25; 1507 | font-size: 30px; 1508 | } 1509 | .c-rich-text h5 { 1510 | font-family: Helvetica; 1511 | font-weight: 700; 1512 | line-height: 1.25; 1513 | font-size: 24px; 1514 | } 1515 | .c-rich-text h6 { 1516 | font-family: Helvetica; 1517 | font-weight: 700; 1518 | line-height: 1.25; 1519 | font-size: 20px; 1520 | } 1521 | .c-rich-text ul { 1522 | list-style-type: disc; 1523 | } 1524 | .c-rich-text ol { 1525 | list-style-type: decimal; 1526 | } 1527 | .c-rich-text a { 1528 | font-weight: 700; 1529 | color: #2083c0; 1530 | } 1531 | .c-rich-text a:hover { 1532 | opacity: 0.5; 1533 | text-decoration: underline; 1534 | } 1535 | .c-rich-text b, .c-rich-text strong { 1536 | font-weight: 700; 1537 | } 1538 | .c-rich-text i, .c-rich-text em { 1539 | font-style: italic; 1540 | } 1541 | @media (min-width: 640px) { 1542 | } 1543 | `); 1544 | }); 1545 | }); 1546 | 1547 | test('variants can be customized', () => { 1548 | return generatePluginCss({ 1549 | theme: { 1550 | colors: { 1551 | 'red': '#f00', 1552 | }, 1553 | }, 1554 | variants: { 1555 | textDecorationStyle: ['hover'], 1556 | textDecorationColor: ['active'], 1557 | ellipsis: ['hover'], 1558 | hyphens: ['active'], 1559 | kerning: ['focus'], 1560 | textUnset: [], 1561 | fontVariantCaps: ['focus', 'responsive'], 1562 | fontVariantNumeric: ['group-hover'], 1563 | fontVariantLigatures: [], 1564 | textRendering: [], 1565 | }, 1566 | }).then(css => { 1567 | expect(css).toMatchCss(` 1568 | .line-solid { 1569 | text-decoration-style: solid; 1570 | } 1571 | .line-double { 1572 | text-decoration-style: double; 1573 | } 1574 | .line-dotted { 1575 | text-decoration-style: dotted; 1576 | } 1577 | .line-dashed { 1578 | text-decoration-style: dashed; 1579 | } 1580 | .line-wavy { 1581 | text-decoration-style: wavy; 1582 | } 1583 | .hover\\:line-solid:hover { 1584 | text-decoration-style: solid; 1585 | } 1586 | .hover\\:line-double:hover { 1587 | text-decoration-style: double; 1588 | } 1589 | .hover\\:line-dotted:hover { 1590 | text-decoration-style: dotted; 1591 | } 1592 | .hover\\:line-dashed:hover { 1593 | text-decoration-style: dashed; 1594 | } 1595 | .hover\\:line-wavy:hover { 1596 | text-decoration-style: wavy; 1597 | } 1598 | .line-red { 1599 | text-decoration-color: #f00; 1600 | } 1601 | .active\\:line-red:active { 1602 | text-decoration-color: #f00; 1603 | } 1604 | .ellipsis { 1605 | text-overflow: ellipsis; 1606 | } 1607 | .no-ellipsis { 1608 | text-overflow: clip; 1609 | } 1610 | .hover\\:ellipsis:hover { 1611 | text-overflow: ellipsis; 1612 | } 1613 | .hover\\:no-ellipsis:hover { 1614 | text-overflow: clip; 1615 | } 1616 | .hyphens-none { 1617 | hyphens: none; 1618 | } 1619 | .hyphens-manual { 1620 | hyphens: manual; 1621 | } 1622 | .hyphens-auto { 1623 | hyphens: auto; 1624 | } 1625 | .active\\:hyphens-none:active { 1626 | hyphens: none; 1627 | } 1628 | .active\\:hyphens-manual:active { 1629 | hyphens: manual; 1630 | } 1631 | .active\\:hyphens-auto:active { 1632 | hyphens: auto; 1633 | } 1634 | .kerning { 1635 | font-kerning: normal; 1636 | } 1637 | .kerning-none { 1638 | font-kerning: none; 1639 | } 1640 | .kerning-auto { 1641 | font-kerning: auto; 1642 | } 1643 | .focus\\:kerning:focus { 1644 | font-kerning: normal; 1645 | } 1646 | .focus\\:kerning-none:focus { 1647 | font-kerning: none; 1648 | } 1649 | .focus\\:kerning-auto:focus { 1650 | font-kerning: auto; 1651 | } 1652 | .font-family-unset { 1653 | font-family: inherit; 1654 | } 1655 | .font-weight-unset { 1656 | font-weight: inherit; 1657 | } 1658 | .font-style-unset { 1659 | font-style: inherit; 1660 | } 1661 | .text-size-unset { 1662 | font-size: inherit; 1663 | } 1664 | .text-align-unset { 1665 | text-align: inherit; 1666 | } 1667 | .leading-unset { 1668 | line-height: inherit; 1669 | } 1670 | .tracking-unset { 1671 | letter-spacing: inherit; 1672 | } 1673 | .text-color-unset { 1674 | color: inherit; 1675 | } 1676 | .text-transform-unset { 1677 | text-transform: inherit; 1678 | } 1679 | .caps-normal { 1680 | font-variant-caps: normal; 1681 | } 1682 | .caps-small { 1683 | font-variant-caps: small-caps; 1684 | } 1685 | .caps-all-small { 1686 | font-variant-caps: all-small-caps; 1687 | } 1688 | .caps-petite { 1689 | font-variant-caps: petite-caps; 1690 | } 1691 | .caps-unicase { 1692 | font-variant-caps: unicase; 1693 | } 1694 | .caps-titling { 1695 | font-variant-caps: titling-caps; 1696 | } 1697 | .focus\\:caps-normal:focus { 1698 | font-variant-caps: normal; 1699 | } 1700 | .focus\\:caps-small:focus { 1701 | font-variant-caps: small-caps; 1702 | } 1703 | .focus\\:caps-all-small:focus { 1704 | font-variant-caps: all-small-caps; 1705 | } 1706 | .focus\\:caps-petite:focus { 1707 | font-variant-caps: petite-caps; 1708 | } 1709 | .focus\\:caps-unicase:focus { 1710 | font-variant-caps: unicase; 1711 | } 1712 | .focus\\:caps-titling:focus { 1713 | font-variant-caps: titling-caps; 1714 | } 1715 | .nums-normal { 1716 | font-variant-numeric: normal; 1717 | } 1718 | .nums-ordinal { 1719 | font-variant-numeric: ordinal; 1720 | } 1721 | .nums-slashed-zero { 1722 | font-variant-numeric: slashed-zero; 1723 | } 1724 | .nums-lining { 1725 | font-variant-numeric: lining-nums; 1726 | } 1727 | .nums-oldstyle { 1728 | font-variant-numeric: oldstyle-nums; 1729 | } 1730 | .nums-proportional { 1731 | font-variant-numeric: proportional-nums; 1732 | } 1733 | .nums-tabular { 1734 | font-variant-numeric: tabular-nums; 1735 | } 1736 | .nums-diagonal-fractions { 1737 | font-variant-numeric: diagonal-fractions; 1738 | } 1739 | .nums-stacked-fractions { 1740 | font-variant-numeric: stacked-fractions; 1741 | } 1742 | .group:hover .group-hover\\:nums-normal { 1743 | font-variant-numeric: normal; 1744 | } 1745 | .group:hover .group-hover\\:nums-ordinal { 1746 | font-variant-numeric: ordinal; 1747 | } 1748 | .group:hover .group-hover\\:nums-slashed-zero { 1749 | font-variant-numeric: slashed-zero; 1750 | } 1751 | .group:hover .group-hover\\:nums-lining { 1752 | font-variant-numeric: lining-nums; 1753 | } 1754 | .group:hover .group-hover\\:nums-oldstyle { 1755 | font-variant-numeric: oldstyle-nums; 1756 | } 1757 | .group:hover .group-hover\\:nums-proportional { 1758 | font-variant-numeric: proportional-nums; 1759 | } 1760 | .group:hover .group-hover\\:nums-tabular { 1761 | font-variant-numeric: tabular-nums; 1762 | } 1763 | .group:hover .group-hover\\:nums-diagonal-fractions { 1764 | font-variant-numeric: diagonal-fractions; 1765 | } 1766 | .group:hover .group-hover\\:nums-stacked-fractions { 1767 | font-variant-numeric: stacked-fractions; 1768 | } 1769 | .ligatures-normal { 1770 | font-variant-ligatures: normal; 1771 | } 1772 | .ligatures-none { 1773 | font-variant-ligatures: none; 1774 | } 1775 | .ligatures-common { 1776 | font-variant-ligatures: common-ligatures; 1777 | } 1778 | .ligatures-no-common { 1779 | font-variant-ligatures: no-common-ligatures; 1780 | } 1781 | .ligatures-discretionary { 1782 | font-variant-ligatures: discretionary-ligatures; 1783 | } 1784 | .ligatures-no-discretionary { 1785 | font-variant-ligatures: no-discretionary-ligatures; 1786 | } 1787 | .ligatures-historical { 1788 | font-variant-ligatures: historical-ligatures; 1789 | } 1790 | .ligatures-no-historical { 1791 | font-variant-ligatures: no-historical-ligatures; 1792 | } 1793 | .ligatures-contextual { 1794 | font-variant-ligatures: contextual; 1795 | } 1796 | .ligatures-no-contextual { 1797 | font-variant-ligatures: no-contextual; 1798 | } 1799 | .text-rendering-auto { 1800 | text-rendering: auto; 1801 | } 1802 | .text-optimize-legibility { 1803 | text-rendering: optimizeLegibility; 1804 | } 1805 | .text-optimize-speed { 1806 | text-rendering: optimizeSpeed; 1807 | } 1808 | .text-geometric-precision { 1809 | text-rendering: geometricPrecision; 1810 | } 1811 | @media (min-width: 640px) { 1812 | .sm\\:caps-normal { 1813 | font-variant-caps: normal; 1814 | } 1815 | .sm\\:caps-small { 1816 | font-variant-caps: small-caps; 1817 | } 1818 | .sm\\:caps-all-small { 1819 | font-variant-caps: all-small-caps; 1820 | } 1821 | .sm\\:caps-petite { 1822 | font-variant-caps: petite-caps; 1823 | } 1824 | .sm\\:caps-unicase { 1825 | font-variant-caps: unicase; 1826 | } 1827 | .sm\\:caps-titling { 1828 | font-variant-caps: titling-caps; 1829 | } 1830 | .sm\\:focus\\:caps-normal:focus { 1831 | font-variant-caps: normal; 1832 | } 1833 | .sm\\:focus\\:caps-small:focus { 1834 | font-variant-caps: small-caps; 1835 | } 1836 | .sm\\:focus\\:caps-all-small:focus { 1837 | font-variant-caps: all-small-caps; 1838 | } 1839 | .sm\\:focus\\:caps-petite:focus { 1840 | font-variant-caps: petite-caps; 1841 | } 1842 | .sm\\:focus\\:caps-unicase:focus { 1843 | font-variant-caps: unicase; 1844 | } 1845 | .sm\\:focus\\:caps-titling:focus { 1846 | font-variant-caps: titling-caps; 1847 | } 1848 | } 1849 | `); 1850 | }); 1851 | }); 1852 | --------------------------------------------------------------------------------