Tailwindcss 進階學習筆記

tags: CSS tailwindcss 2024

本篇主要只記載一些我個人覺得比較特殊需要筆記的內容~並不是整份文件喔!

Main Document

Layout

<!-- 控制屬性如 background, border 等是否要跨行連貫處理 -->
<span class="box-decoration-clone bg-gradient-to-r from-indigo-600 to-pink-500 text-white px-2 ...">
  Hello<br />
  World
</span>
<!-- Preventing parent overscrolling -->
<div class="overscroll-contain ...">Well, let me tell you something, ...</div>
<!-- 控制 grid 元素擺放位置 -->
<div class="grid grid-flow-row-dense grid-cols-3 grid-rows-3 ...">
  <div class="col-span-2">01</div>
  <div class="col-span-2">02</div>
  <div>03</div>
  <div>04</div>
  <div>05</div>
</div>
<!-- 添加元素間的間距(就不用自己用 margin 判斷最後一個不用加之類的問題) -->
<div class="flex space-x-4 ...">
  <div>01</div>
  <div>02</div>
  <div>03</div>
</div>

Flexbox & Grid

<!-- Subgrid 會自動偵測內部元素,在需要的時候自動擴展空間使用,不需要時則壓縮為 0px 隱藏區間 -->
<div role="menu" class="grid grid-cols-[auto_1fr]">
  <a href="#" class="grid-cols-subgrid col-span-2">
    <svg class="mr-2">...</svg>
    <span class="col-start-2">Account</span>
  </a>
  <a href="#" class="grid-cols-subgrid col-span-2">
    <svg class="mr-2">...</svg>
    <span class="col-start-2">Settings</span>
  </a>
  <a href="#" class="grid-cols-subgrid col-span-2">
    <span class="col-start-2">Sign out</span>
  </a>
</div>

關於 Subgrid 的官方說明可參考這邊open in new window,關於 MDN Web 相關 Subgrid 的原生說明可參考這邊open in new window

Sizing

<!-- w-16, h-16 寫起來很煩,直接用 size-16 吧 -->
<div class="size-16 ...">
  <img />
</div>

Typography

<!-- 可以自定義圖片作為 ul 的 list style -->
<ul class="list-image-[url(carrot.png)] ...">
  <li>5 cups chopped Porcini mushrooms</li>
  <!-- ... -->
</ul>

<!-- all -->
<p class="text-lg leading-7 ...">
<!-- shorthand -->
<p class="text-lg/7 ...">
  So I started to walk into the water. I won't lie to you boys, I was terrified. But
  I pressed on, and as I made my way past the breakers a strange calm came over me.
  I don't know if it was divine intervention or the kinship of all living things but
  I tell you Jerry at that moment, I <em>was</em> a marine biologist.
</p>

Backgrounds

<!-- 漸層背景斷點 -->
<div class="bg-gradient-to-r from-indigo-500 from-10% via-purple-500 via-30% to-pink-500 to-90% ...">
  <!-- ... -->
</div>

Borders

<button class="... ring-offset-2 ring-2">Button A</button>

Tables

<table>
  <caption class="caption-bottom">
    Table 3.1: Professional wrestlers and their signature moves.
  </caption>
  <!-- ... -->
</table>

Effects

<!-- 製作圖層疊加效果時的好幫手 -->
<div class="flex justify-center -space-x-14">
  <div class="mix-blend-multiply bg-blue-400 ..."></div>
  <div class="mix-blend-multiply bg-pink-400 ..."></div>
</div>

Interactivity

<!-- 覆蓋原生瀏覽器的 input checkbox 樣式之類 -->
<label>
  <input type="checkbox" checked> Browser default
</label>
<label>
  <input type="checkbox" class="accent-pink-500" checked> Customized
</label>
<!-- 覆蓋原生瀏覽器的 select 樣式之類 -->
<select class="appearance-none row-start-1 col-start-1 bg-slate-50 dark:bg-slate-800 ...">
  <option>Yes</option>
  <option>No</option>
  <option>Maybe</option>
</select>
<!-- 全局平滑滾動效果 -->
<html class="scroll-smooth">
  <!-- ... -->
</html>
<!-- 滾動停頓點效果(類似輪播的效果) -->
<div class="snap-x ...">
  <div class="snap-center ...">
    <img src="https://images.unsplash.com/photo-1604999565976-8913ad2ddb7c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=320&h=160&q=80" />
  </div>
  <div class="snap-center ...">
    <img src="https://images.unsplash.com/photo-1540206351-d6465b3ac5c1?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=320&h=160&q=80" />
  </div>
  <div class="snap-center ...">
    <img src="https://images.unsplash.com/photo-1622890806166-111d7f6c7c97?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=320&h=160&q=80" />
  </div>
</div>

Advanced Topics

Custom Classes

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer utilities {
  .content-auto {
    content-visibility: auto;
  }
}
<div class="lg:content-auto">
  <!-- ... -->
</div>

Styling children

<!--
  從父層選擇下一次的所有元素套用,一般而言推薦直接把樣式寫在子元素上,
  但如果因為一些原因子元素非我們可控制時就可以這樣寫
-->
<ul class="*:rounded-full *:border *:border-sky-100 *:bg-sky-50 *:px-2 *:py-0.5 dark:text-sky-300 dark:*:border-sky-500/15 dark:*:bg-sky-500/10 ...">
  <li>Sales</li>
  <li>Marketing</li>
  <li>SEO</li>
  <!-- ... -->
</ul>

Creating custom modifiers

let plugin = require('tailwindcss/plugin')

module.exports = {
  // ...
  plugins: [
    plugin(function ({ addVariant }) {
      // Add a `third` variant, ie. `third:pb-0`
      addVariant('third', '&:nth-child(3)')
    })
  ]
}

Arbitrary Variants (specify element selector)

<div class="[&>*]:p-4">...</>
<div class="[&>p]:mt-0 ">...</div>
<div class="[&:nth-child(3)]:py-0">
  <!-- ... -->
</div>

<!-- reset html input[type=number] style -->
<input type="number" class="focus:outline-none focus:ring-0 border-0 [appearance:none] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" />

<input type="text" className="[&::placeholder]:text-red-500" />

Appendix

Writing plugins

客製化 TailwindCSS 的 plugin

const plugin = require('tailwindcss/plugin')

module.exports = {
  // ...
  plugins: [
    plugin(function ({ addBase, addComponents, addUtilities, addVariant, matchVariant, theme }) {
      addBase({
        html: {
          'font-family':
            '-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Microsoft JhengHei, Helvetica Neue, Helvetica, Arial, sans-serif'
        },
        'h1': {
          fontSize: theme('fontSize.2xl'),
        },
        'h2': {
          fontSize: theme('fontSize.xl'),
        },
      })
      addComponents({
        '.card': {
          backgroundColor: theme('colors.white'),
          borderRadius: theme('borderRadius.lg'),
          padding: theme('spacing.6'),
          boxShadow: theme('boxShadow.xl'),
          // if you have install "postcss-nested"
          '&:hover': {
            boxShadow: '0 10px 15px rgba(0,0,0,0.2)',
          },
          '@media (min-width: 500px)': {
            borderRadius: '.5rem',
          }
        }
      })
      addUtilities({
        '.content-auto': {
          contentVisibility: 'auto',
        },
        '.text-primary': {
          '@apply text-gray-800': {},
        }
      })
      addVariant('optional', '&:optional')
      addVariant('hocus', ['&:hover', '&:focus'])
      addVariant('starting-style', ['@starting-style'])
      matchVariant(
        'nth',
        (value) => {
          return `&:nth-child(${value})`;
        },
        {
          values: {
            1: '1',
            2: '2',
            3: '3',
          }
        }
      );
    })
  ]
}

Customization

Extend Screens

import type { Config } from "tailwindcss";
import { screens } from 'tailwindcss/defaultTheme';

const config: Config = {
  theme: {
    screens: {
      xs: '441px',
      ...screens
    }
  }
}

export default config;

Content

Safelist

雖然最佳狀態下希望根據使用狀況引入所需的最小 bundle class,但某些內容可能是動態產生,以致於 tailwind 沒辦法在掃描時發現他們,這時可以利用 safelist 確保某些 class 被強制引入

module.exports = {
  content: [
    './pages/**/*.{html,js}'
    './components/**/*.{html,js}',
  ],
  safelist: [
    'bg-red-500',
    'text-3xl',
    'lg:text-4xl',
  ]
  // ...
}

Configuration Demo

const tailwindcssSafeArea = require('tailwindcss-safe-area');
const tailwindForms = require('@tailwindcss/forms');
const tailwindAspectRatio = require('@tailwindcss/aspect-ratio');

const defaultFontFamily = [
  "-apple-system",
  "BlinkMacSystemFont",
  "Segoe UI",
  "Helvetica Neue",
  "Arial",
  "sans-serif",
];

module.exports = {
  content: ['./src/**/*.{js,ts,jsx,tsx}'],
  darkMode: false, // or 'media' or 'class'
  theme: {
    fontFamily: {
      sans: defaultFontFamily,
    },
    fontSize: {
      xs: ['0.625rem', '1rem'],
      'title-xs': ['0.75rem', '1rem'],
      28: ['1.75rem', '2.25rem'],
    },
    colors: {
      transparent: 'transparent',
      current: 'currentColor',
      black: '#000000',
      white: '#FFFFFF',
      primary: '#06C755',
      positive: '#3CC926',
      negative: '#FF334B',
      disabled: '#E4E4E4',
      warn: '#FF6F37',
      category: 'var(--category-color)',
      gray: {
        100: '#FCFCFC',
        200: '#EFEFEF',
        300: '#DFDFDF',
        350: '#C8C8C8',
        400: '#B7B7B7',
        500: '#949494',
        600: '#777777',
        700: '#555555',
        800: '#303030',
        900: '#111111',
      },
    },
    opacity: {
      0: '0',
      50: '.5',
      100: '1',
    },
    borderRadius: {
      DEFAULT: '3px',
      100: '3px',
      200: '5px',
      300: '7px',
      circle: '9999px',
      none: 'none',
    },
    boxShadow: {
      DEFAULT: '0px 1px 6px rgba(0, 0, 0, 0.12)',
      'on-white-100':
        '0px 0px 2px rgba(0, 0, 0, 0.07), 0px 1px 2px rgba(0, 0, 0, 0.07)',
      inner: 'inset 0 2px 4px 0 rgb(0 0 0 / 0.05)',
      none: 'none',
    },
    extend: {
      borderWidth: {
        0.5: '0.5px',
        3: '3px',
      },
      spacing: {
        13: '3.25rem',
        'fixed-offset': 'var(--fixed-offset)',
        'fixed-offset-top': 'var(--fixed-offset-top)',
      },
      colors: {
        category: 'var(--category-color)',
        test: {
          blue: '#3657BB',
          yellow: '#FFC53D',
        },
      },
      outlineOffset: {
        '-1': '-1px',
      },
    },
  },
  variants: {
    extend: {},
  },
  plugins: [tailwindcssSafeArea, tailwindForms, tailwindAspectRatio],
  // //-- if used with 3rd party UI component library
  // corePlugins: {
  //   preflight: false,
  // },
  // //-- if tailwind is the main custom css system for you in project
  // important: true,
};
Last Updated:
Contributors: johnnywang