Skip to content

Commit

Permalink
feat: adding ui-plugins to better handle TailwindCSS (#370)
Browse files Browse the repository at this point in the history
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced a new UI plugin library `@versini/ui-plugins` for enhancing
UI components with custom styles and Tailwind CSS configurations.
- Added `@versini/ui-plugins` as a dependency in the documentation
package to leverage new UI enhancements.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
aversini authored Mar 2, 2024
1 parent 9c2e9be commit 0666841
Show file tree
Hide file tree
Showing 18 changed files with 513 additions and 10 deletions.
3 changes: 2 additions & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
"packages/ui-hooks": "2.2.0",
"packages/ui-system": "1.0.1",
"packages/ui-private": "1.1.0",
"packages/ui-icons": "1.1.0"
"packages/ui-icons": "1.1.0",
"packages/ui-plugins": "0.0.0"
}
1 change: 1 addition & 0 deletions packages/documentation/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@tailwindcss/typography": "0.5.10",
"@versini/ui-components": "workspace:../ui-components",
"@versini/ui-icons": "workspace:../ui-icons",
"@versini/ui-plugins": "workspace:../ui-plugins",
"@versini/ui-system": "workspace:../ui-system",
"clsx": "2.1.0",
"react": "18.2.0",
Expand Down
12 changes: 4 additions & 8 deletions packages/documentation/tailwind.config.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
/** @type {import('tailwindcss').Config} */

import { twPlugin as componentsPlugin } from "@versini/ui-components/dist/utilities";
import { twPlugin as systemPlugin } from "@versini/ui-system/dist/utilities";
import { twPlugin } from "@versini/ui-plugins";

export default systemPlugin.merge(
componentsPlugin.merge({
darkMode: "selector",
content: ["./src/**/*.{js,ts,jsx,tsx}", "./.ladle/**/*.tsx"],
}),
);
export default twPlugin.merge({
content: ["./src/**/*.{js,ts,jsx,tsx}", "./.ladle/**/*.tsx"],
});
27 changes: 27 additions & 0 deletions packages/ui-plugins/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh

## Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:

- Configure the top-level `parserOptions` property like this:

```js
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: ['./tsconfig.json', './tsconfig.node.json'],
tsconfigRootDir: __dirname,
},
```

- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
33 changes: 33 additions & 0 deletions packages/ui-plugins/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "@versini/ui-plugins",
"version": "0.0.0",
"license": "MIT",
"author": "Arno Versini",
"publishConfig": {
"access": "public"
},
"homepage": "https://github.com/aversini/ui-components",
"repository": {
"type": "git",
"url": "[email protected]:aversini/ui-components.git"
},
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build:check": "tsc",
"build:js": "vite build",
"build:types": "tsup",
"build": "npm-run-all --serial clean build:check build:js build:types",
"clean": "rimraf dist",
"dev:js": "vite build --watch --mode development",
"dev:types": "tsup --watch src",
"dev": "npm-run-all clean --parallel dev:js dev:types",
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0 --fix --color",
"test:coverage": "echo \"WARNING: no test specified\" && exit 0",
"test": "echo \"WARNING: no test specified\" && exit 0"
}
}
33 changes: 33 additions & 0 deletions packages/ui-plugins/src/plugins/customCSS.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export const customCSS = {
[`.av-text-input-wrapper label[aria-hidden="true"],
.av-text-area-wrapper label[aria-hidden="true"]`]: {
/* move the label inline */
transform: "translate(18px, 0) scale(1)",
transformOrigin: "top left",
transition: "var(--av-text-area-wrapper-transition, all 0.2s ease-out)",
},
'.av-text-input:focus + label[aria-hidden="true"],\n\t.av-text-input:not(:placeholder-shown) + label[aria-hidden="true"],\n\t.av-text-area:focus + label[aria-hidden="true"],\n\t.av-text-area:not(:placeholder-shown) + label[aria-hidden="true"]':
{
transform:
"translate(18px, var(--av-text-area-label, -25px)) scale(0.75)",
},

'.av-text-input-simple:focus + label[aria-hidden="true"],\n\t.av-text-input-simple:not(:placeholder-shown) + label[aria-hidden="true"]':
{
transform:
"translate(18px, var(--av-text-area-label, -12px)) scale(0.75)",
},

".av-text-input-helper-text,\n\t.av-text-area-helper-text": {
transform:
"translate(18px, var(--av-text-area-helper-text, 32px))\n\t\t\tscale(0.75)",
transformOrigin: "top left",
},
".av-text-input__control--right,\n\t.av-text-area__control--right": {
right: "18px",
},
"@keyframes blink": { "50%": { fill: "transparent" } },
".av-spinner__dot": { animation: "1s blink infinite" },
".av-spinner__dot:nth-child(2)": { animationDelay: "250ms" },
".av-spinner__dot:nth-child(3)": { animationDelay: "500ms" },
};
1 change: 1 addition & 0 deletions packages/ui-plugins/src/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { twPlugin } from "./tailwindPlugin";
187 changes: 187 additions & 0 deletions packages/ui-plugins/src/plugins/tailwindPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import typography from "@tailwindcss/typography";
import { converter } from "culori";
import plugin from "tailwindcss/plugin";
import type { Config, OptionalConfig } from "tailwindcss/types/config";

import { customCSS } from "./customCSS";
import { tokens } from "./tokens";

type TailwindConfig = {
content: string[];
} & OptionalConfig;

const parse = converter("rgb");

const dynamicColors = () => {
const result: { [key: string]: string } = {};
Object.entries(tokens.colors).forEach(([name, color]) => {
const rgb = parse(color);
const variable = `--av-${name}`;
const fallbackValue = rgb
? `${rgb.r * 255} ${rgb.g * 255} ${rgb.b * 255}`
: "0 0 0";
result[name] = `var(${variable}, rgb(${fallbackValue} / <alpha-value>))`;
});
return result;
};

const dynamicMargins = () => {
const allowed = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 24, 28, 32, 36, 44,
48, 52, 56, 60, 64, 72, 80, 96,
];
const margins: string[] = [];
allowed.forEach((num) => {
margins.push(`mt-${num}`);
margins.push(`mr-${num}`);
margins.push(`mb-${num}`);
margins.push(`ml-${num}`);
});
return margins;
};

const myComponentLibraryConfig = {
theme: {
extend: {
colors: dynamicColors(),
typography: ({ theme }: { theme: (arg0: string) => any }) => ({
DEFAULT: {
css: {
maxWidth: "inherit",
"h1, h2, h3, h4, h5, h6": {
fontFamily: "Open Sans, ui-sans-serif, system-ui, sans-serif",
},
blockquote: {
borderLeftWidth: "6px",
},
"blockquote p": {
fontFamily: "Georgia, Cambria, Times New Roman, Times, serif",
},
li: {
fontSize: "1rem",
},
pre: {
marginTop: "2rem",
marginBottom: "2rem",
fontSize: "0.875rem",
lineHeight: "1.25rem",
},
code: {
fontFamily:
'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
},
},
},
light: {
css: {
"--tw-prose-body": tokens.colors["copy-light"],
"--tw-prose-headings": tokens.colors["copy-medium"],
"--tw-prose-lead": tokens.colors["copy-light"],
"--tw-prose-links": tokens.colors["copy-light"],
"--tw-prose-bold": tokens.colors["copy-light"],
"--tw-prose-counters": tokens.colors["copy-medium"],
"--tw-prose-bullets": tokens.colors["copy-medium"],
"--tw-prose-hr": tokens.colors["copy-medium"],
"--tw-prose-quotes": tokens.colors["copy-light"],
"--tw-prose-quote-borders": tokens.colors["copy-light"],
"--tw-prose-captions": tokens.colors["copy-light"],
"--tw-prose-code": tokens.colors["copy-light"],
"--tw-prose-pre-code": tokens.colors["copy-lighter"],
"--tw-prose-pre-bg": tokens.colors["surface-medium"],
"--tw-prose-kbd": tokens.colors["copy-light"],
li: {
color: tokens.colors["copy-light"],
},
},
},
lighter: {
css: {
"--tw-prose-body": tokens.colors["copy-lighter"],
"--tw-prose-headings": tokens.colors["copy-light"],
"--tw-prose-lead": tokens.colors["copy-lighter"],
"--tw-prose-links": tokens.colors["copy-lighter"],
"--tw-prose-bold": tokens.colors["copy-lighter"],
"--tw-prose-counters": tokens.colors["copy-light"],
"--tw-prose-bullets": tokens.colors["copy-light"],
"--tw-prose-hr": tokens.colors["copy-light"],
"--tw-prose-quotes": tokens.colors["copy-lighter"],
"--tw-prose-quote-borders": tokens.colors["copy-lighter"],
"--tw-prose-captions": tokens.colors["copy-lighter"],
"--tw-prose-code": tokens.colors["copy-lighter"],
"--tw-prose-pre-code": tokens.colors["copy-lighter"],
"--tw-prose-pre-bg": tokens.colors["surface-darker"],
"--tw-prose-kbd": tokens.colors["copy-lighter"],
li: {
color: tokens.colors["copy-lighter"],
},
},
},
dark: {
css: {
"--tw-prose-body": theme("colors.slate[800]"),
"--tw-prose-headings": theme("colors.slate[900]"),
"--tw-prose-lead": theme("colors.slate[700]"),
"--tw-prose-links": theme("colors.slate[900]"),
"--tw-prose-bold": theme("colors.slate[900]"),
"--tw-prose-counters": theme("colors.slate[600]"),
"--tw-prose-bullets": theme("colors.slate[400]"),
"--tw-prose-hr": theme("colors.slate[300]"),
"--tw-prose-quotes": theme("colors.slate[900]"),
"--tw-prose-quote-borders": theme("colors.slate[300]"),
"--tw-prose-captions": theme("colors.slate[700]"),
"--tw-prose-code": theme("colors.slate[900]"),

"--tw-prose-pre-code": tokens.colors["copy-lighter"],
"--tw-prose-pre-bg": tokens.colors["surface-medium"],

// "--tw-prose-pre-code": theme("colors.slate[100]"),
// "--tw-prose-pre-bg": theme("colors.slate[900]"),

"--tw-prose-kbd": theme("colors.slate[800]"),
li: {
color: tokens.colors["copy-dark"],
},
},
},
}),
},
},
};

const tailwindContentPath = [
(__dirname + "/**/*.{js,ts,jsx,tsx}").replace(
"ui-plugins/dist",
"ui-system/dist",
),
(__dirname + "/**/*.{js,ts,jsx,tsx}").replace(
"ui-plugins/dist",
"ui-components/dist",
),
];

const tailwindPlugins = [
typography,
plugin(function ({ addUtilities }) {
addUtilities(customCSS);
}, myComponentLibraryConfig),
];

const tailwindSafelist = [...dynamicMargins()];

export const twPlugin = {
content: tailwindContentPath,
safelist: tailwindSafelist,
plugins: tailwindPlugins,

merge: (config: TailwindConfig) => {
const safelist = tailwindSafelist;
const content = tailwindContentPath;
const plugins = tailwindPlugins;

config.safelist = [...(config.safelist || []), ...safelist];
config.content = [...(config.content || []), ...content];
config.plugins = [...(config.plugins || []), ...plugins];

return config as Config;
},
};
Loading

0 comments on commit 0666841

Please sign in to comment.