Rapid designing with Tailwind CSS

Rapid designing with Tailwind CSS

Component-focused CSS frameworks such as Bootstrap get you up and running quickly, but often lack the tools necessary for designing custom interfaces. For this people usually just use plain (S)CSS, which gets the job done but can become difficult to maintain in the long run.

Enter Tailwind, a utility-first CSS framework that approaches building complex components from a constrained set of primitive utilities. What sets Tailwind apart is the vast range of CSS properties it covers, a powerful PostCSS-backed configuration API and excellent documentation that explains CSS along the way. This talk will show Tailwind's unique approach to various aspects of design (spacing, colors, pseudo-classes, responsive design and more) and compare it with existing solutions.

376e4eb9dc6c2e33d1330262edc4f109?s=128

Janko Marohnić

May 29, 2020
Tweet

Transcript

  1. Tailwind CSS Rapid designing with utility-first approach

  2. Janko Marohnić • Ruby developer • open source contributor •

    inexperienced in CSS • mainly used Bootstrap
  3. Tailwind CSS • utility-first CSS framework • maps closely to

    CSS properties • PostCSS plugin • created by Adam Wathan in 2017
  4. Bootstrap 1 CSS 2 Tailwind 3

  5. Utility-First

  6. None
  7. CSS

  8. <div class="vacation-card"> <img class="vacation-card-image" src="..."> <div class="vacation-card-info"> <div class="vacation-card-eyebrow"> Private

    Villa </div> <div class="vacation-card-title"> <a href="..." class="vacation-card-link"> Relaxing All-Inclusive Resort in Cancun </a> </div> <div class="vacation-card-price"> $299 USD per night </div> </div> </div>
  9. .vacation-card { width: 16rem; margin: 0 auto; } .vacation-card-info {

    margin-top: .5rem; } .vacation-card-image { border-radius: .25rem; } .vacation-card-title { font-weight: 700; color: #4a5568; line-height: 1.375; } .vacation-card-eyebrow { font-size: .75rem; color: #718096; text-transform: uppercase; font-weight: 700; } .vacation-card-link:hover { text-decoration: underline; } .vacation-card-price { margin-top: .5rem; font-size: .875rem; color: #718096; }
  10. Bootstrap

  11. <div class="vacation-card mx-auto"> <img class="rounded" src="..."> <div class="mt-2"> <div class="vacation-card-eyebrow

    text-uppercase font-weight-bold"> Private Villa </div> <div class="vacation-card-title font-weight-bold"> <a href="..." class="vacation-card-link"> Relaxing All-Inclusive Resort in Cancun </a> </div> <div class="vacation-card-price mt-2"> $299 USD per night </div> </div> </div>
  12. .vacation-card { width: 16rem; } .vacation-card-eyebrow { font-size: .75rem; color:

    #718096; } .vacation-card-title { color: #4a5568; line-height: 1.375; } .vacation-card-link:hover { text-decoration: underline; } .vacation-card-price { font-size: .875rem; color: #718096; }
  13. <div class="w-64 mx-auto"> <img class="rounded" src="..."> <div class="mt-2"> <div class="text-xs

    text-gray-600 uppercase font-bold"> Private Villa </div> <div class="font-bold text-gray-700 leading-snug"> <a href="..." class="hover:underline"> Relaxing All-Inclusive Resort in Cancun </a> </div> <div class="mt-2 text-sm text-gray-600"> $299 USD per night </div> </div> </div>
  14. Why?

  15. Embraces connection between HTML and CSS 1

  16. No need to keep inventing new class names 2 .card-inner-outer-wrapper

    .container-wrapper-container .box-control-outer-container
  17. Localized styling makes changes safer 3

  18. Your CSS stops growing 4

  19. You’re designing with constraints 5 font-size: .875rem .text-sm color: #feb2b2

    .text-red-300 height: 100vh .h-screen
  20. Documentation teaches CSS

  21. Bootstrap Tailwind .d-block .d-inline-block .d-flex ... .block .inline-block .flex ...

    .justify-content-start .align-items-center .align-self-center ... .justify-start .items-center .self-center ... .text-uppercase .font-italic .font-weight-bold ... .uppercase .italic .font-bold ... .position-absolute .position-relative ... .absolute .relative ...
  22. DRY

  23. <div class="card"> <img class="card-img-top" src="/apple"> <div class="card-body"> <div class="card-title">Apple</div> <div

    class="card-text">Apple</div> ... </div> </div> <div class="card"> <img class="card-img-top" src="/kiwi"> <div class="card-body"> <div class="card-title">Kiwi</div> <div class="card-text">Kiwi</div> ... </div> </div> page1.html page2.html
  24. <div class="card"> <img class="card-img-top" src={img}> <div class="card-body"> <div class="card-title">{title}</div> <div

    class="card-text">{text}</div> ... </div> </div> <Card img="/apple" title="Apple" text="Apple" /> Card.jsx Card.vue card.erb card.php ... page1.html <Card img="/kiwi" title="Kiwi" text="Kiwi" /> page2.html
  25. <div class="flex rounded bg-white shadow-sm"> <img class="rounded-t w-full" src={img}> <div

    class="flex-auto p-4 break-words"> <div class="mb-3">{title}</div> <div class="break-words">{text}</div> ... </div> </div> <Card img="/apple" title="Apple" text="Apple" /> Card.jsx Card.vue card.erb card.php ... page1.html <Card img="/kiwi" title="Kiwi" text="Kiwi" /> page2.html
  26. .btn-blue { background-color: #4299e1; color: white; font-weight: 700; padding: 0.5rem

    1rem; border-radius: 0.25rem; } <a href="..." class="btn-blue">Some Link</a> <form ...> ... <button type="submit" class="btn-blue">Save</button> </form>
  27. .btn-blue { @apply bg-blue-500 text-white font-bold py-2 px-4 rounded; }

    <a href="..." class="btn-blue">Some Link</a> <form ...> ... <button type="submit" class="btn-blue">Save</button> </form> PostCSS directive
  28. Spacing & Sizing

  29. .vacation-card { margin: 15px; padding: 18em; width: 77%; height: 16rem;

    }
  30. .m-* .mt-* .mb-* .ml-* .mr-* .mx-* .my-* Margin Padding .p-*

    .pt-* .pb-* .pl-* .pr-* .px-* .py-* Suffix Amount *-0 *-1 *-2 *-3 *-4 *-5 0 0.25rem 0.5rem 1rem 1.5rem 3rem
  31. .m-* .mt-* .mb-* .ml-* .mr-* .mx-* .my-* Margin Padding .p-*

    .pt-* .pb-* .pl-* .pr-* .px-* .py-* Suffix Amount *-0 *-1 *-2 *-3 *-4 *-5 *-6 *-8 *-10 *-12 *-16 *-20 ... *-64 0 0.25rem 0.5rem 1.75rem 1rem 1.25rem 1.5rem 2rem 2.5rem 3rem 4rem 5rem ... 16rem Width .w-* Height .h-*
  32. Class Property .w-25 .w-50 .w-75 .w-100 width: 25% width: 50%

    width: 75% width: 100%
  33. Class Property .w-1/2 .w-1/3 .w-2/3 .w-1/4 .w-2/4 .w-3/4 .w-1/5 .w-2/5

    .w-3/5 ... .w-11/12 .w-full width: 50% width: 33.333333% width: 66.666667% width: 25% width: 50% width: 75% width: 20% width: 40% width: 60% ... width: 91.66667% width: 100%
  34. <div class="row"> <div class="col-6">...</div> <div class="col-4">...</div> <div class="col-2">...</div> </div>

  35. <div class="flex -mx-4"> <div class="w-6/12 px-4">...</div> <div class="w-4/12 px-4">...</div> <div

    class="w-2/12 px-4">...</div> </div>
  36. <div class="flex space-x-8"> <div class="w-3/6">...</div> <div class="w-2/6">...</div> <div class="w-1/6">...</div> </div>

  37. <label for="phone">Phone Number</label> <div class="col-6 col-md-4 col-lg-2"> <input type="tel" id="phone"

    name="phone" /> </div> </div>
  38. <label for="phone">Phone Number</label> <div class="w-24"> <input type="tel" id="phone" name="phone" />

    </div> </div>
  39. module.exports = { theme: { extend: { spacing: { '18':

    '4.5rem', } } } } ... .m-16 .m-18 .m-20 ... ... .p-16 .p-18 .p-20 ... ... .w-16 .w-18 .w-20 ... ... .h-16 .h-18 .h-20 ... ... 4rem 4.5rem 5rem ... tailwind.config.js
  40. module.exports = { theme: { spacing: { 'sm': '8px', 'md':

    '12px', 'lg': '16px', 'xl': '24px', } } } tailwind.config.js m-sm m-md m-lg m-xl p-sm p-md p-lg m-xl w-sm w-md w-lg w-xl h-sm h-md h-lg h-xl
  41. Colors

  42. #4b0082 indigo rgb(76, 0, 130) rgba(76, 0, 130, 1.0) hsl(275,

    100%, 25%) darken(#4b0082, 10%) lighten(#4b0082, 15%) tint(#4b0082, 20%) shade(#4b0082, 30%) ... ...
  43. *-primary *-secondary *-success *-danger *-warning *-info *-light *-dark ... .text-*

    .bg-* .border-*
  44. Taken from Refactoring UI

  45. Taken from Refactoring UI

  46. None
  47. .text-red-300 .bg-blue-500 .border-green-700 .btn-indigo { background-color: theme('colors.indigo.500'); } PostCSS function

  48. module.exports = { theme: { colors: { indigo: { lighter:

    '#b3bcf5', default: '#5c6ac4', dark: '#202e78', } } } } tailwind.config.js .text-indigo-lighter .text-indigo-default .text-indigo-dark .bg-indigo-lighter .bg-indigo-default .bg-indigo-dark .border-indigo-lighter .border-indigo-default .border-indigo-dark
  49. const { colors } = require('tailwindcss/defaultTheme') module.exports = { theme:

    { colors: { indigo: { lighter: colors.indigo['300'], default: colors.indigo['500'], dark: colors.indigo['700'], }, blue: { ...colors.blue, '900': '#1e3656', } } } } tailwind.config.js
  50. Pseudo-Classes

  51. <button class="btn-blue">Save</button> .btn-blue { ... } .btn-blue:hover { background-color: #2B6CB0;

    } .btn-blue:focus { background-color: #2B6CB0; box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5); }
  52. <button class="hover:bg-blue-700 focus:shadow-outline ..."> Save </button> <a class="hover:underline active:text-indigo-300 ...">

    Some Link </a> <a class="border-b hover:border-teal-700 ..."> Tab </a>
  53. .hover\:bg-blue-500:hover { background-color: #4299e1; } .focus\:shadow-outline:focus { box-shadow: 0 0

    0 3px rgba(66, 153, 225, 0.5); } .active\:text-indigo-300:active { color: #a3bffa; }
  54. @variants hover, focus { .hyphens-none { hyphens: none; } .hyphens-manual

    { hyphens: manual; } .hyphens-auto { hyphens: auto; } } PostCSS directive
  55. .hyphens-none { hyphens: none; } .hover\:hyphens-none:hover { hyphens: none; }

    .focus\:hyphens-none:focus { hyphens: none; } .hyphens-manual { hyphens: manual; } .hover\:hyphens-manual:hover { hyphens: manual; } .focus\:hyphens-manual:focus { hyphens: manual; } .hyphens-auto { hyphens: auto; } .hover\:hyphens-auto:hover { hyphens: auto; } .focus\:hyphens-auto:focus { hyphens: auto; }
  56. <div class="hyphens-none hover:hyphens-auto"></div>

  57. Responsive Design

  58. @media (min-width: 640px) { ... } @media (min-width: 768px) {

    ... } @media (min-width: 1024px) { ... } @media (min-width: 1280px) { ... }
  59. .col-sm-8 .col-md-6 .col-lg-4 .col-xl-2 .d-sm-block .d-md-inline-block .d-lg-flex .d-xl-none .flex-sm-row .justify-content-md-center

    .align-items-lg-baseline .align-self-xl-end .row-cols-sm-8 .row-cols-md-6 .row-cols-lg-4 .row-cols-xl-2
  60. .d-sm-block .d-md-inline-block .d-lg-flex .d-xl-none

  61. .sm:block .md:inline-block .lg:flex .xl:hidden

  62. .sm:text-red-500 .md:text-center .lg:text-lg .xl:font-bold .sm:mt-0 .md:px-4 .lg:w-40 .xl:h-24 .sm:self-start .md:justify-center

    .lg:items-baseline .xl:content-around .sm:border-2 .md:border-blue-500 .lg:border-dashed .xl:rounded-full .sm:grid-cols-2 .md:grid-cols-4 .lg:grid-cols-6 .xl:grid-cols-8 .sm:flex-grow .md:flex-shrink .lg:flex-initial .xl:flex-auto .sm:bg-gray-100 .md:bg-fixed .lg:bg-opacity-50 .xl:bg-left .sm:transition .md:duration-100 .lg:scale-50 .xl:translate-100 .sm:block .md:inline-block .lg:flex .xl:hidden
  63. <ul class="flex flex-col lg:flex-row"> <li class="w-full lg:w-auto">Foo</li> <li class="w-full lg:w-auto">Bar</li>

    <li class="w-full lg:w-auto">Baz</li> </ul>
  64. @responsive { .hyphens-none { hyphens: none; } .hyphens-manual { hyphens:

    manual; } .hyphens-auto { hyphens: auto; } } PostCSS directive
  65. @media (min-width: 640px) { .sm\:hyphens-none { hyphens: none; } .sm\:hyphens-manual

    { hyphens: manual; } .sm\:hyphens-auto { hyphens: auto; } } @media (min-width: 768px) { .md\:hyphens-none { hyphens: none; } .md\:hyphens-manual { hyphens: manual; } .md\:hyphens-auto { hyphens: auto; } } @media (min-width: 1024px) { .lg\:hyphens-none { hyphens: none; } .lg\:hyphens-manual { hyphens: manual; } .lg\:hyphens-auto { hyphens: auto; } } @media (min-width: 1280px) { .xl\:hyphens-none { hyphens: none; } .xl\:hyphens-manual { hyphens: manual; } .xl\:hyphens-auto { hyphens: auto; } }
  66. <div class="hyphens-auto lg:hyphens-manual"></div>

  67. Bundle Size

  68. ~1.8MB uncompressed ~148KB minified and compressed with Gzip ~44KB compressed

    with Brotli
  69. PurgeCSS 1

  70. module.exports = { purge: [ 'src/**/*.html', 'src/**/*.vue', 'src/**/*.jsx', ], theme:

    {}, variants: {}, plugins: [], } tailwind.config.js
  71. You must list all files in your project that reference

    any of Tailwind class names ! You must not use string concatenation to create class names ! ✕ <div class="text-{error ? 'red' : 'green'}-600"></div> <div class="{error ? 'text-red-600' : 'text-green-600'}"></div> ✓
  72. Reducing features 2

  73. module.exports = { theme: { borderColor: theme => ({ 'green':

    theme('colors.green.300') }), divideColor: theme => ({ 'green': theme('colors.green.300') }), translate: { '1': '0.25rem' }, }, corePlugins: [ 'placeholderColor', 'placeholderOpacity', 'textOpacity', 'skew', 'scale', 'rotate', 'gap', 'gridColumn', 'gridColumnStart', 'gridColumnEnd', 'gridRow', 'gridRowStart', 'gridRowEnd', ].reduce((list, plugin) => ({ [plugin]: false, ...list }), {}), variants: { textColor: ['hover', 'focus'], backgroundColor: ['hover', 'focus'], borderRadius: [], borderOpacity: ['hover', 'focus'], transitionDuration: [], transitionDelay: [], }, } tailwind.config.js
  74. 0 450 900 1350 1800 Uncompressed Gzip Original Reduced 1800K

    400K 148KB 56K
  75. Recap • maps closely to CSS • naming optimized for

    writing • generous defaults (colors, spacing) • pseudo-class & responsive variants (for each class) • PostCSS directives & functions • advanced JavaScript-based configuration
  76. Resources • https://tailwindcss.com/docs • https://tailwindcss.com/screencasts • https://tailwindcss-custom-forms.netlify.app/ • https://marketplace.visualstudio.com/items?itemName=bradlc.vscode- tailwindcss

    • https://github.com/aniftyco/awesome-tailwindcss • https://adamwathan.me/css-utility-classes-and-separation-of-concerns/ • Adam Wathan’s YouTube channel • Steve Schoger's design tips