├── .eslintrc.js
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── TODO.md
├── package.json
├── pnpm-lock.yaml
├── src
├── animate.ts
├── decoration.ts
├── future.ts
├── index.ts
└── scrollbars.ts
├── tailwind.config.js
└── tsconfig.json
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | commonjs: true,
4 | es6: true,
5 | node: true,
6 | },
7 | globals: {
8 | Atomics: "readonly",
9 | SharedArrayBuffer: "readonly",
10 | },
11 | parserOptions: {
12 | ecmaVersion: 2018,
13 | },
14 | rules: {},
15 | };
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | *.html
4 | *.css
5 | tags
6 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # CHANGELOG
2 |
3 | ## [Unreleased]
4 |
5 | - Remove classes made obsolete by Tailwind CSS v2.4
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Brandon Pittman
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # tailwindcss-plugin-fancy
2 |
3 | This plugin merely wraps up a collection of other plugins I've written for
4 | Tailwind that makes my life/job easier.
5 |
6 | ## Usage
7 |
8 | ```javascript
9 | const fancy = require("tailwindcss-plugin-fancy");
10 |
11 | // tailwind.config.js
12 | module.export = {
13 | // ...
14 | plugins: [fancy],
15 | };
16 | ```
17 |
18 | ## Animation
19 |
20 | The Tailwind animation utility is nice, but it lacks the flexibility of the
21 | transition utilities. This plugin adds support for the following:
22 |
23 | - animation-name
24 | - animation-duration
25 | - animation-delay
26 | - animation-fill-mode
27 | - animation-direction
28 | - animation-iteration
29 | - animation-timing-function
30 | - animation-play-state
31 |
32 | ### Utilities
33 |
34 | ```css
35 | .animate-${name} {
36 | /* the name is a keyframe just how the standard Tailwind animation plugin requires */
37 | }
38 |
39 | .animate-duration-${n} {
40 | /* duration-${n} */
41 | }
42 |
43 | .animate-delay-${n} {
44 | /* delay-${n} */
45 | }
46 |
47 | .animate-ease-${ease} {
48 | animation-timing-function: /* ease-${linear, in, out, in-out} */
49 | }
50 |
51 | .running {
52 | animation-play-state: running
53 | }
54 |
55 | .paused {
56 | animation-play-state: paused
57 | }
58 |
59 | .direction-${normal, reverse, alternate, alternate-reverse} {
60 | animation-direction: /* normal, reverse, alternate, alternate-reverse */
61 | }
62 |
63 | .iterate-${n} {
64 | animation-iteration-count: ${n} /* 0-12 and infinite */
65 | }
66 |
67 | .fill-${none, forwards, backwards, both} {
68 | animation-fill-mode: /* none, forwards, backwards, both */
69 | }
70 | ```
71 |
72 | `animation-timing-function` gets support for steps!
73 |
74 | ```css
75 | .animate-steps-5 {
76 | animation-timing-function: steps(5);
77 | }
78 |
79 | .animate-step-start {
80 | animation-timing-function: steps(1, jump-start);
81 | }
82 |
83 | .animate-step-end {
84 | animation-timing-function: steps(1, jump-end);
85 | }
86 | ```
87 |
88 | Steps go from 0–12, then 15, 30, 45, and 60 by default.
89 |
90 | Add your own in `tailwind.config.js`.
91 |
92 | ```javascript
93 | theme: {
94 | extend: {
95 | animate: {
96 | steps: [
97 | 17, // creates a steps(17) class as .animate-step-17
98 | [47, "jump-both"] // creates a steps(47, jump-both) class as .animate-step-47-jump-both
99 | }
100 | }
101 | }
102 | ```
103 |
104 | ### Make It Your Own
105 |
106 | The delay, duration, and timing function utilities pull from the transtion
107 | counterparts in your theme. To add to the iteration counts, provide something
108 | like the following in `tailwind.config.js`.
109 |
110 | ```javascript
111 | theme: {
112 | animate: {
113 | iterate: ["1.5", "2.5"];
114 | }
115 | }
116 | ```
117 |
118 | ## Stylable Scrollbars
119 |
120 | Style your scrollbars!
121 |
122 | ```css
123 | /* W3C scrollbar styling standard (Firefox) */
124 | body {
125 | scrollbar-width: thin; /* "auto" or "thin" */
126 | scrollbar-color: blue orange; /* scroll thumb & track */
127 | }
128 |
129 | /* Webkit family of browsers (Safari, Chrome, etc.) */
130 | body::-webkit-scrollbar {
131 | width: 16px; /* width of the entire scrollbar */
132 | }
133 | body::-webkit-scrollbar-track {
134 | background: orange; /* color of the tracking area */
135 | }
136 | body::-webkit-scrollbar-thumb {
137 | background-color: blue; /* color of the scroll thumb */
138 | border-radius: 20px; /* roundness of the scroll thumb */
139 | border: 3px solid orange; /* creates padding around scroll thumb */
140 | }
141 | ```
142 |
143 | ### Utilities
144 |
145 | ```css
146 | /* You need to add this first one to make the other utilities work, a la Tailwind's transform utility.) */
147 | .scrollbar .scrollbar-thumb-$color /* var(--scrollbar-thumb) */
148 | .scrollbar-track-$color /* var(--scrollbar-track) */ .scrollbar-auto
149 | /* var(--scrollbar-width-webkit) 16px for -webkit */ .scrollbar-thin
150 | /* var(--scrollbar-width-webkit) 11px for -webkit */ .scrollbar-none
151 | /* var(--scrollbar-width-webkit) */
152 | /* The next two utilities will only work as expected on Chrome and Safari.
153 | Firefox follows the W3C standard which treats horizontal and vertical
154 | scrollabar width equally. On Firefox, they will hide BOTH scrollbars. */
155 | .scrollbar-x-none .scrollbar-y-none;
156 | ```
157 |
158 | The `$color` bit can be any color in your theme. For best results, apply the
159 | utilities to the `html` tag in your templates. Since they're just utilities,
160 | you can apply different ones to scrollable elements within your site to have
161 | multiple styles.
162 |
163 | ## Tailwind 2077
164 |
165 | These are settings that I could see being added to the Tailwind standard config
166 | in the future.
167 |
168 | ### Touch
169 |
170 | There's a `touch` variant that targets `@media(hover: none)`.
171 |
172 | ### Not Touch
173 |
174 | There's a `not-touch` variant that targets `@media(hover: hover)`.
175 |
176 | ### Bleed
177 |
178 | Adds `.bleed` and `.bleed-grid` components to to make blog-style full bleed
179 | images easier to handle.
180 |
181 | ### Stripes
182 |
183 | This adds the nifty `bg-stripes` utils from Tailwind's documentation. Use
184 | `bg-stripes` to turn the utility on and then add a `bg-stripes-${color}`
185 | utility to actually set the stripe color.
186 |
187 | You can also use `bg-stripes-{0, 45, 90, 135}` to control the angle.
188 |
189 | ### `word-break: keep-all`
190 |
191 | CJK has serious issues with linebreaks. Use the `.break-keep-all` util and
192 | place `` elements wherever a line _could_ break and see some nice
193 | results!
194 |
195 | ```html
196 |
197 | この文書がちょっと
198 |
199 | 長いかもしれません。
200 |
201 | ```
202 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | # Fancy Tailwind
2 |
3 | ## To-do
4 |
5 | - Ditch TSDX
6 | - Refactor modules
7 |
8 | ## In Progress
9 |
10 | ## Done
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "3.2.4",
3 | "license": "MIT",
4 | "main": "dist/index.js",
5 | "typings": "dist/index.d.ts",
6 | "files": [
7 | "dist",
8 | "src"
9 | ],
10 | "engines": {
11 | "node": ">=10"
12 | },
13 | "scripts": {
14 | "start": "tsdx watch --target node",
15 | "build": "tsdx build --target node",
16 | "test": "tsdx test",
17 | "lint": "tsdx lint",
18 | "prepare": "tsdx build --target node",
19 | "size": "size-limit",
20 | "analyze": "size-limit --why"
21 | },
22 | "peerDependencies": {
23 | "postcss": "^8.2.8",
24 | "tailwindcss": "^3.0.0"
25 | },
26 | "nano-staged": {
27 | "*.{json,md,ts,tsx}": [
28 | "prettier --write"
29 | ]
30 | },
31 | "simple-git-hooks": {
32 | "pre-commit": "./node_modules/.bin/nano-staged"
33 | },
34 | "name": "tailwindcss-plugin-fancy",
35 | "author": "Brandon Pittman",
36 | "module": "dist/tailwindcss-plugin-fancy.esm.js",
37 | "size-limit": [
38 | {
39 | "path": "dist/tailwindcss-plugin-fancy.cjs.production.min.js",
40 | "limit": "10 KB"
41 | },
42 | {
43 | "path": "dist/tailwindcss-plugin-fancy.esm.js",
44 | "limit": "10 KB"
45 | }
46 | ],
47 | "devDependencies": {
48 | "@size-limit/preset-small-lib": "^4.12.0",
49 | "autoprefixer": "^10.3.1",
50 | "nano-staged": "^0.5.0",
51 | "postcss": "^8.3.6",
52 | "prettier": "^2.5.1",
53 | "simple-git-hooks": "^2.7.0",
54 | "size-limit": "^4.12.0",
55 | "tailwindcss": "^3.0.0",
56 | "tsdx": "^0.14.1",
57 | "tslib": "^2.3.0",
58 | "typescript": "^4.3.5"
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/animate.ts:
--------------------------------------------------------------------------------
1 | import plugin from "tailwindcss/plugin";
2 |
3 | const defaultIterate = [
4 | "0",
5 | "1",
6 | "2",
7 | "3",
8 | "4",
9 | "5",
10 | "6",
11 | "7",
12 | "8",
13 | "9",
14 | "10",
15 | "11",
16 | "12",
17 | "15",
18 | "30",
19 | "45",
20 | "60",
21 | "infinite",
22 | ];
23 |
24 | const defaultSteps = [
25 | "0",
26 | "1",
27 | "2",
28 | "3",
29 | "4",
30 | "5",
31 | "6",
32 | "7",
33 | "8",
34 | "9",
35 | "10",
36 | "11",
37 | "12",
38 | "15",
39 | "30",
40 | "45",
41 | "60",
42 | ];
43 |
44 | export default plugin(
45 | ({ addUtilities, theme, e }) => {
46 | const delays = {
47 | ...Object.fromEntries(
48 | Object.entries(theme("transitionDelay")).map(([k, v]) => [
49 | `.animate-delay-${k}`,
50 | { animationDelay: v },
51 | ])
52 | ),
53 | ".animate-delay-2000": { animationDelay: "2000ms" },
54 | ".animate-delay-3000": { animationDelay: "3000ms" },
55 | ".animate-delay-4000": { animationDelay: "4000ms" },
56 | ".animate-delay-5000": { animationDelay: "5000ms" },
57 | };
58 |
59 | const durations = {
60 | ...Object.fromEntries(
61 | Object.entries(theme("transitionDelay")).map(([k, v]) => [
62 | `.animate-duration-${k}`,
63 | { animationDuration: v },
64 | ])
65 | ),
66 | ".animate-duration-2000": { animationDuration: "2000ms" },
67 | ".animate-duration-3000": { animationDuration: "3000ms" },
68 | ".animate-duration-4000": { animationDuration: "4000ms" },
69 | ".animate-duration-5000": { animationDuration: "5000ms" },
70 | };
71 |
72 | const names = Object.fromEntries(
73 | Object.keys(theme("keyframes")).map((key) => [
74 | `.animate-${key}`,
75 | { animationName: key },
76 | ])
77 | );
78 |
79 | const timingFuctions = {
80 | ".animate-step-start": { animationTimingFunction: "jump-start" },
81 | ".animate-step-end": { animationTimingFunction: "jump-end" },
82 | ...Object.fromEntries(
83 | Object.entries(theme("transitionTimingFunction")).map(([k, v]) => [
84 | k === "DEFAULT" ? ".animate-ease" : `.animate-ease-${k}`,
85 | { animationTimingFunction: v },
86 | ])
87 | ),
88 | ...Object.fromEntries(
89 | [...defaultSteps, ...theme("animate").steps].map((step) =>
90 | Array.isArray(step)
91 | ? [
92 | `.animate-step-${step[0]}-${step[1]}`,
93 | { animationTimingFunction: `steps(${step[0]}, ${step[1]})` },
94 | ]
95 | : [
96 | `.animate-step-${step}`,
97 | { animationTimingFunction: `steps(${step})` },
98 | ]
99 | )
100 | ),
101 | };
102 |
103 | const playStates = {
104 | ".running": {
105 | animationPlayState: "running",
106 | },
107 | ".paused": {
108 | animationPlayState: "paused",
109 | },
110 | };
111 |
112 | const modes = Object.fromEntries(
113 | ["none", "forwards", "backwards", "both"].map((mode) => [
114 | `.fill-${mode}`,
115 | { animationFillMode: mode },
116 | ])
117 | );
118 |
119 | const directions = Object.fromEntries(
120 | ["normal", "reverse", "alternate", "alternate-reverse"].map(
121 | (direction) => [
122 | `.direction-${direction}`,
123 | { animationDirection: direction },
124 | ]
125 | )
126 | );
127 |
128 | const iterations = Object.fromEntries(
129 | [...defaultIterate, ...theme("animate").iterate].map((count) => [
130 | `.${e(`iterate-${count}`)}`,
131 | { animationIterationCount: count },
132 | ])
133 | );
134 |
135 | addUtilities(
136 | {
137 | ...delays,
138 | ...durations,
139 | ...names,
140 | ...timingFuctions,
141 | ...playStates,
142 | ...modes,
143 | ...directions,
144 | ...iterations,
145 | ".animate-none": {
146 | animationName: "none",
147 | },
148 | ".animate-spin": {
149 | animationName: "spin",
150 | },
151 | ".animate-ping": {
152 | animationName: "ping",
153 | },
154 | ".animate-pulse": {
155 | animationName: "pulse",
156 | },
157 | ".animate-bounce": {
158 | animationName: "bounce",
159 | },
160 | ".animate-warp": {
161 | animationName: "bg-warp",
162 | },
163 | },
164 | ["responsive", "hover", "focus", "group-hover", "group-focus"]
165 | );
166 | },
167 | {
168 | theme: {
169 | animate: {
170 | iterate: defaultIterate,
171 | steps: defaultSteps,
172 | },
173 | },
174 | }
175 | );
176 |
--------------------------------------------------------------------------------
/src/decoration.ts:
--------------------------------------------------------------------------------
1 | import plugin from "tailwindcss/plugin";
2 |
3 | export default plugin(({ addUtilities, variants, theme }) => {
4 | let { colors, spacing } = require("tailwindcss/defaultTheme");
5 |
6 | colors = {
7 | ...colors,
8 | ...theme("colors"),
9 | };
10 |
11 | const decorationStyles = {
12 | ".overline": {
13 | textDecorationLine: "overline",
14 | },
15 | ".decoration-skip-none": {
16 | textDecorationSkip: "none",
17 | },
18 | ".decoration-skip-objects": {
19 | textDecorationSkip: "objects",
20 | },
21 | ".decoration-skip-spaces": {
22 | textDecorationSkip: "spaces",
23 | },
24 | ".decoration-skip-edges": {
25 | textDecorationSkip: "edges",
26 | },
27 |
28 | ".decoration-skip-ink": {
29 | textDecorationSkipInk: "auto",
30 | },
31 | ".decoration-skip-ink-none": {
32 | textDecorationSkipInk: "none",
33 | },
34 |
35 | ".underline-double": {
36 | textDecorationStyle: "double",
37 | },
38 | ".underline-dotted": {
39 | textDecorationStyle: "dotted",
40 | },
41 | ".underline-dashed": {
42 | textDecorationStyle: "dashed",
43 | },
44 | ".underline-wavy": {
45 | textDecorationStyle: "wavy",
46 | },
47 | };
48 |
49 | const decorationColors = {
50 | ".decoration-current": {
51 | textDecorationColor: "currentColor",
52 | },
53 | };
54 |
55 | const decorationLines = {};
56 |
57 | for (const [key, value] of Object.entries(spacing)) {
58 | decorationLines[`.underline-thickness-${key}`] = {
59 | textDecorationThickness: value,
60 | };
61 |
62 | decorationLines[`.underline-offset-${key}`] = {
63 | textUnderlineOffset: value,
64 | };
65 | }
66 |
67 | for (const [key, value] of Object.entries(colors)) {
68 | if (typeof value === "string") {
69 | decorationColors[`.decoration-${key}`] = {
70 | textDecorationColor: value,
71 | };
72 | } else {
73 | for (const [k, newV] of Object.entries(colors[key])) {
74 | decorationColors[`.decoration-${key}-${k}`] = {
75 | textDecorationColor: newV,
76 | };
77 | }
78 | }
79 | }
80 |
81 | const decorations = {
82 | ...decorationLines,
83 | ...decorationColors,
84 | ...decorationStyles,
85 | };
86 |
87 | addUtilities(decorations, variants("decoration", ["responsive", "hover"]));
88 | });
89 |
--------------------------------------------------------------------------------
/src/future.ts:
--------------------------------------------------------------------------------
1 | import plugin from "tailwindcss/plugin";
2 | import postcss from "postcss";
3 | import { default as flattenColorPalette } from "tailwindcss/lib/util/flattenColorPalette";
4 | import { toRgba } from "tailwindcss/lib/util/withAlphaVariable";
5 |
6 | export default plugin(
7 | ({ addVariant, addComponents, addUtilities, theme, e }) => {
8 | const ariaCurrentValues = [
9 | "page",
10 | "step",
11 | "location",
12 | "date",
13 | "time",
14 | "true",
15 | ];
16 |
17 | addVariant("current", ({ modifySelectors, separator }) => {
18 | modifySelectors(({ className }) => {
19 | return [...ariaCurrentValues]
20 | .map(
21 | (val) =>
22 | `.${e(`current${separator}${className}`)}[aria-current="${val}"]`
23 | )
24 | .join(", ");
25 | });
26 | });
27 |
28 | addVariant("expanded", ({ modifySelectors, separator }) => {
29 | modifySelectors(({ className }) => {
30 | return `.${e(
31 | `expanded${separator}${className}`
32 | )}[aria-expanded="true"], [aria-expanded="true"] > .${e(
33 | `expanded${separator}${className}`
34 | )}`;
35 | });
36 | });
37 |
38 | addVariant("selected", ({ modifySelectors, separator }) => {
39 | modifySelectors(({ className }) => {
40 | return `.${e(
41 | `expanded${separator}${className}`
42 | )}[aria-selected="true"], [aria-selected="true"] > .${e(
43 | `expanded${separator}${className}`
44 | )}`;
45 | });
46 | });
47 |
48 | const stripes = {
49 | ".bg-stripes": {
50 | backgroundImage:
51 | "linear-gradient(var(--stripes-angle, 45deg), var(--stripes-color) 12.50%, transparent 12.50%, transparent 50%, var(--stripes-color) 50%, var(--stripes-color) 62.50%, transparent 62.50%, transparent 100%)",
52 | backgroundSize: "5.66px 5.66px",
53 | },
54 | ".bg-stripes-0": { "--stripes-angle": "0deg" },
55 | ".bg-stripes-45": { "--stripes-angle": "45deg" },
56 | ".bg-stripes-90": { "--stripes-angle": "90deg" },
57 | ".bg-stripes-135": { "--stripes-angle": "135deg" },
58 | };
59 |
60 | const addColor = (name: string, color: string) =>
61 | (stripes[`.bg-stripes-${name}`] = { "--stripes-color": color });
62 |
63 | const colors = flattenColorPalette(theme("backgroundColor"));
64 | for (let name in colors) {
65 | try {
66 | const [r, g, b, a] = toRgba(colors[name]);
67 | if (a !== undefined) {
68 | addColor(name, colors[name]);
69 | } else {
70 | addColor(name, `rgba(${r}, ${g}, ${b}, 0.4)`);
71 | }
72 | } catch (_) {
73 | addColor(name, colors[name]);
74 | }
75 | }
76 |
77 | addUtilities(stripes, ["responsive"]);
78 |
79 | const fullBleed = {
80 | ".bleed-grid": {
81 | display: "grid",
82 | gridTemplateColumns: "1fr min(65ch, 100%) 1fr",
83 | "& > *": {
84 | gridColumn: "2",
85 | },
86 | },
87 | ".bleed": {
88 | width: "100%",
89 | gridColumn: "1/4",
90 | },
91 | };
92 |
93 | addComponents(fullBleed, ["responsive"]);
94 |
95 | addUtilities({
96 | "@keyframes bg-warp": {
97 | from: { "background-position": "right" },
98 | to: { "background-position": "left" },
99 | },
100 | });
101 | addUtilities(
102 | {
103 | ".bg-skinny": {
104 | backgroundSize: "0% 100%",
105 | },
106 | ".bg-flat": {
107 | backgroundSize: "100% 0%",
108 | },
109 | ".bg-full": {
110 | backgroundSize: "100% 100%",
111 | },
112 | ".bg-fat": {
113 | backgroundSize: "200% 200%",
114 | },
115 | ".bg-jumbo": {
116 | backgroundSize: "400% 400%",
117 | },
118 | },
119 | ["responsive"]
120 | );
121 |
122 | addUtilities({
123 | ".w-128": { width: "32rem" },
124 | ".h-128": { height: "32rem" },
125 | ".break-keep-all": {
126 | "word-break": "keep-all",
127 | },
128 | });
129 |
130 | addVariant("touch", ({ container, separator }) => {
131 | const supportsRule = postcss.atRule({
132 | name: "media",
133 | params: "(hover: none)",
134 | });
135 | supportsRule.append(container.nodes);
136 | container.append(supportsRule);
137 | supportsRule.walkRules((rule) => {
138 | rule.selector = `.${e(`touch${separator}${rule.selector.slice(1)}`)}`;
139 | });
140 | });
141 | addVariant("not-touch", ({ container, separator }) => {
142 | const supportsRule = postcss.atRule({
143 | name: "media",
144 | params: "(hover: hover)",
145 | });
146 | supportsRule.append(container.nodes);
147 | container.append(supportsRule);
148 | supportsRule.walkRules((rule) => {
149 | rule.selector = `.${e(
150 | `not-touch${separator}${rule.selector.slice(1)}`
151 | )}`;
152 | });
153 | });
154 | }
155 | );
156 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import plugin from "tailwindcss/plugin";
2 | import future from "./future";
3 | import scrollbars from "./scrollbars";
4 | import animate from "./animate";
5 |
6 | const plugins = [future, scrollbars, animate];
7 |
8 | module.exports = plugin(
9 | (helpers: any) => {
10 | plugins.forEach((plugin) => plugin.handler(helpers));
11 | },
12 | {
13 | variants: {
14 | ...animate.config.variants,
15 | },
16 | theme: {
17 | extend: {
18 | transitionDelay: {
19 | 2000: "2000ms",
20 | 3000: "3000ms",
21 | 4000: "4000ms",
22 | 5000: "5000ms",
23 | },
24 | transitionDuration: {
25 | 2000: "2000ms",
26 | 3000: "3000ms",
27 | 4000: "4000ms",
28 | 5000: "5000ms",
29 | },
30 | minWidth: (theme) => theme("width"),
31 | maxWidth: (theme) => theme("width"),
32 | minHeight: (theme) => theme("height"),
33 | maxHeight: (theme) => theme("height"),
34 | ...animate.config.theme,
35 | },
36 | },
37 | }
38 | );
39 |
--------------------------------------------------------------------------------
/src/scrollbars.ts:
--------------------------------------------------------------------------------
1 | import plugin from "tailwindcss/plugin";
2 |
3 | export default plugin(({ addUtilities, theme }) => {
4 | const scrollbarColors = {};
5 |
6 | for (const [key, v] of Object.entries(theme("colors"))) {
7 | if (typeof v === "string") {
8 | scrollbarColors[`.scrollbar-track-${key}`] = {
9 | "--scrollbar-track": v,
10 | };
11 |
12 | scrollbarColors[`.scrollbar-thumb-${key}`] = {
13 | "--scrollbar-thumb": v,
14 | };
15 | } else {
16 | for (const [k, newV] of Object.entries(theme("colors")[key])) {
17 | scrollbarColors[`.scrollbar-track-${key}-${k}`] = {
18 | "--scrollbar-track": newV,
19 | };
20 |
21 | scrollbarColors[`.scrollbar-thumb-${key}-${k}`] = {
22 | "--scrollbar-thumb": newV,
23 | };
24 | }
25 | }
26 | }
27 |
28 | addUtilities({
29 | ".scrollbar": {
30 | "--scrollbar-thumb": "#b5b5b5",
31 | "--scrollbar-track": "#f9f9f9",
32 | "--scrollbar-width": "auto",
33 | "--scrollbar-width-webkit": "16px",
34 | "--scrollbar-height-webkit": "16px",
35 |
36 | scrollbarWidth: "var(--scrollbar-width, initial)",
37 | scrollbarColor:
38 | "var(--scrollbar-thumb, initial) var(--scrollbar-track, initial)",
39 |
40 | "&::-webkit-scrollbar": {
41 | width: "var(--scrollbar-width-webkit)",
42 | height: "var(--scrollbar-height-webkit)",
43 | },
44 |
45 | "&::-webkit-scrollbar-track": {
46 | backgroundColor: "var(--scrollbar-track)",
47 | },
48 |
49 | "&::-webkit-scrollbar-thumb": {
50 | backgroundColor: "var(--scrollbar-thumb)",
51 | border: "3px solid var(--scrollbar-track)",
52 | borderRadius: "8px",
53 | },
54 | },
55 | ".scrollbar-auto": {
56 | scrollbarWidth: "auto",
57 | "&::-webkit-scrollbar": {
58 | display: "initial",
59 | },
60 | "--scrollbar-width-webkit": "16px",
61 | "--scrollbar-height-webkit": "16px",
62 | },
63 | ".scrollbar-thin": {
64 | scrollbarWidth: "thin",
65 | "--scrollbar-width-webkit": "11px",
66 | "--scrollbar-height-webkit": "11px",
67 | },
68 | ".scrollbar-none": {
69 | scrollbarWidth: "none",
70 | "&::-webkit-scrollbar": {
71 | display: "none",
72 | },
73 | },
74 | ".scrollbar-y-none": {
75 | scrollbarWidth: "none",
76 | "--scrollbar-width-webkit": "0px",
77 | },
78 | ".scrollbar-x-none": {
79 | scrollbarWidth: "none",
80 | "--scrollbar-height-webkit": "0px",
81 | },
82 | ...scrollbarColors,
83 | });
84 | }, {});
85 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | purge: ["./index.html"],
3 | mode: "jit",
4 | darkMode: false, // or 'media' or 'class'
5 | theme: {
6 | extend: {},
7 | },
8 | variants: {
9 | extend: {},
10 | },
11 | plugins: [require(".")],
12 | };
13 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs
3 | "include": ["src", "types"],
4 | "compilerOptions": {
5 | "module": "esnext",
6 | "lib": ["dom", "esnext"],
7 | "importHelpers": true,
8 | // output .d.ts declaration files for consumers
9 | "declaration": true,
10 | // output .js.map sourcemap files for consumers
11 | "sourceMap": true,
12 | // match output dir to input dir. e.g. dist/index instead of dist/src/index
13 | "rootDir": "./src",
14 | // stricter type-checking for stronger correctness. Recommended by TS
15 | "strict": false,
16 | // linter checks for common issues
17 | "noImplicitReturns": true,
18 | "noFallthroughCasesInSwitch": true,
19 | // noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative
20 | "noUnusedLocals": true,
21 | "noUnusedParameters": true,
22 | // use Node's module resolution algorithm, instead of the legacy TS one
23 | "moduleResolution": "node",
24 | // transpile JSX to React.createElement
25 | "jsx": "react",
26 | // interop between ESM and CJS modules. Recommended by TS
27 | "esModuleInterop": true,
28 | // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS
29 | "skipLibCheck": true,
30 | // error out if import and file system have a casing mismatch. Recommended by TS
31 | "forceConsistentCasingInFileNames": true,
32 | // `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc`
33 | "noEmit": true
34 | }
35 | }
36 |
--------------------------------------------------------------------------------