{"id":27976905,"url":"https://github.com/lloydrichards/base_lit-with-tailwind","last_synced_at":"2026-05-06T19:10:02.099Z","repository":{"id":287690475,"uuid":"852247967","full_name":"lloydrichards/base_lit-with-tailwind","owner":"lloydrichards","description":"Template for how to setup lit using vite and tailwind.","archived":false,"fork":false,"pushed_at":"2025-04-13T10:14:37.000Z","size":79,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-13T11:23:32.299Z","etag":null,"topics":["lit","tailwindcss","typescript","vite","web-components"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lloydrichards.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-09-04T13:37:33.000Z","updated_at":"2025-04-13T10:13:59.000Z","dependencies_parsed_at":"2025-04-13T11:23:34.731Z","dependency_job_id":null,"html_url":"https://github.com/lloydrichards/base_lit-with-tailwind","commit_stats":null,"previous_names":["lloydrichards/base_lit-with-tailwind"],"tags_count":2,"template":true,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lloydrichards%2Fbase_lit-with-tailwind","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lloydrichards%2Fbase_lit-with-tailwind/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lloydrichards%2Fbase_lit-with-tailwind/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lloydrichards%2Fbase_lit-with-tailwind/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lloydrichards","download_url":"https://codeload.github.com/lloydrichards/base_lit-with-tailwind/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252983760,"owners_count":21835758,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["lit","tailwindcss","typescript","vite","web-components"],"created_at":"2025-05-08T01:42:08.877Z","updated_at":"2026-05-06T19:10:02.092Z","avatar_url":"https://github.com/lloydrichards.png","language":"TypeScript","funding_links":[],"categories":["Starter Templates"],"sub_categories":[],"readme":"# How to: Setup Lit with Tailwind v4\n\n## 1. Create a vite project\n\nTo create a vite project, run the following command:\n\n```bash\nbun create vite@latest\n```\n\nMake sure to select the `lit` framework when prompted, and use TypeScript.\n\nOnce setup, navigate to the project directory and install the required\ndependencies:\n\n```bash\ncd my-vite-project\nbun install\n```\n\n## 2. Structure the project\n\ncurrently, the project structure should have a `public` and `src` directory.\nVite provides you with and example my-element.ts file in the `src` directory and\nan `index.html` file in the root directory.\n\nFirst move the index.html file to the `src` directory.\n\n```diff\n .\n ├── 📁 public\n ├── 📂 src\n │   ├── 📁 assets\n │   ├── index.css\n+│   ├── index.html\n │   ├── my-element.ts\n │   └── vite-env.d.ts\n ├── .gitignore\n-├── index.html\n ├── package.json\n └── tsconfig.json\n```\n\nNow create a `lib` directory and move the `assets`. Now create a components\nfolder inside lib and move the `my-element.ts` file to the `lib` directory.\nFinally create a `main.ts` file in the `lib` directory and export the\n`my-element.ts` file.\n\n```diff\n .\n+├── 📂 lib\n+│   ├── 📁 assets\n+│   ├── 📂 components\n+│   │   └── my-element.ts\n+│   └── main.ts\n ├── 📁 public\n ├── 📂 src\n-│   ├── 📁 assets\n │   ├── index.css\n │   ├── index.html\n-│   ├── my-element.ts\n │   └── vite-env.d.ts\n ├── .gitignore\n ├── package.json\n └── tsconfig.json\n```\n\nAnd modify the `index.html` file to point to the `lib/my-element.ts` file.\n\n```diff\n  \u003chead\u003e\n    \u003cmeta charset=\"UTF-8\" /\u003e\n    \u003clink rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" /\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /\u003e\n    \u003ctitle\u003eVite + Lit + TS\u003c/title\u003e\n     \u003clink rel=\"stylesheet\" href=\"/src/index.css\" /\u003e\n+    \u003cscript type=\"module\" src=\"/lib/main.ts\"\u003e\u003c/script\u003e\n  \u003c/head\u003e\n```\n\nFinally, modify the `tsconfig.json` file to point to the `lib` directory.\n\n```diff\n {\n   \"compilerOptions\": {\n     \"target\": \"ES2020\",\n     \"experimentalDecorators\": true,\n     \"useDefineForClassFields\": false,\n     \"module\": \"ESNext\",\n     \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"],\n     \"skipLibCheck\": true,\n\n     /* Bundler mode */\n     \"moduleResolution\": \"bundler\",\n     \"allowImportingTsExtensions\": true,\n     \"isolatedModules\": true,\n     \"moduleDetection\": \"force\",\n     \"noEmit\": true,\n\n     /* Linting */\n     \"strict\": true,\n     \"noUnusedLocals\": true,\n     \"noUnusedParameters\": true,\n     \"noFallthroughCasesInSwitch\": true\n   },\n+  \"include\": [\"src\", \"lib\"]\n }\n```\n\n## 3. Create a vite.config.ts file\n\nFirst lets install the required dependencies:\n\n```bash\nbun add -D vite-plugin-dts vite-tsconfig-paths\n```\n\nAt the root of the project, create a `vite.config.ts` file and add the\nfollowing:\n\n```typescript\nimport { defineConfig } from \"vite\";\nimport { resolve } from \"path\";\nimport dts from \"vite-plugin-dts\";\nimport tsconfigPaths from \"vite-tsconfig-paths\";\n\nexport default defineConfig({\n  plugins: [tsconfigPaths(), dts({ rollupTypes: true })],\n  build: {\n    copyPublicDir: false,\n    lib: {\n      entry: resolve(__dirname, \"lib/main.ts\"),\n      name: \"MyElement\",\n      fileName: \"my-element\",\n    },\n    rollupOptions: {\n      external: [\"react\", \"react-dom\", \"react/jsx-runtime\"],\n      output: {\n        globals: {\n          react: \"React\",\n          \"react-dom\": \"ReactDOM\",\n          \"react/jsx-runtime\": \"react/jsx-runtime\",\n        },\n      },\n    },\n  },\n  server: {\n    open: \"/src/index.html\",\n  },\n});\n```\n\nYou can now test building the project by running the following command:\n\n```bash\nbun run build\n```\n\nAnd you should see the output in the `dist` directory. (make sure to add the\ndist directory to the `.gitignore` file)\n\n```diff\n .\n+├── 📂 dist\n+│   ├── my-element.d.ts\n+│   ├── my-element.js\n+│   └── my-element.umd.cjs\n ├── 📁 lib\n ├── 📁 public\n ├── 📁 src\n ├── .gitignore\n ├── package.json\n └── tsconfig.json\n```\n\nFinally, update the `package.json` file to point to the `dist/` files.\n\n```diff\n{\n  \"name\": \"my-lit-element\",\n- \"private\": true,\n+ \"version\": \"0.0.1\",\n  \"type\": \"module\",\n+  \"files\": [\n+   \"dist\"\n+ ],\n+ \"main\": \"./dist/my-element.umd.cjs\",\n+ \"module\": \"./dist/my-element.js\",\n+ \"exports\": {\n+   \".\": {\n+     \"import\": \"./dist/my-element.js\",\n+     \"require\": \"./dist/my-element.umd.cjs\"\n+   }\n+ },\n  \"scripts\": {\n    \"dev\": \"vite \",\n    \"build\": \"tsc \u0026\u0026 vite build\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"lit\": \"^3.2.0\"\n  },\n  \"devDependencies\": {\n    \"typescript\": \"^5.5.3\",\n    \"vite\": \"^5.4.1\",\n    \"vite-plugin-dts\": \"^4.1.0\",\n    \"vite-tsconfig-paths\": \"^5.0.1\"\n  }\n}\n```\n\n## 4. Add Tailwind CSS\n\nFirst install the required dependencies:\n\n```bash\nbun add -D tailwindcss @tailwindcss/vite\n```\n\nAdd the tailwind plugin to the `vite.config.ts` file:\n\n```diff\nimport { defineConfig } from \"vite\";\nimport { resolve } from \"path\";\nimport dts from \"vite-plugin-dts\";\nimport tsconfigPaths from \"vite-tsconfig-paths\";\n+import tailwindcss from \"@tailwindcss/vite\";\n\nexport default defineConfig({\n+  plugins: [tsconfigPaths(), dts({ rollupTypes: true }), tailwindcss()],\n  // ...rest of config\n});\n```\n\nIn the `lib` directory, create a `shared` folder and add two files:\n`tailwindMixin.ts` and `tailwindMixin.d.ts`. and create a `styles` folder and\nadd a new file `tailwind.global.css`.\n\n```diff\n .\n ├── 📂 lib\n │   ├── 📁 assets\n │   ├── 📂 components\n │   │   └── my-element.ts\n+│   ├── 📂 shared\n+│   │   ├── tailwindMixin.d.ts\n+│   │   └── tailwindMixin.ts\n+│   ├── 📂 styles\n+│   │   └── tailwind.global.css\n │   └── main.ts\n ├── 📁 public\n ├── 📁 src\n ├── .gitignore\n ├── package.json\n └── tsconfig.json\n```\n\n### tailwindMixin.ts\n\n```typescript\nimport { adoptStyles, type LitElement, unsafeCSS } from \"lit\";\nimport tailwindCss from \"../styles/tailwind.global.css?inline\";\n\ndeclare global {\n  // biome-ignore lint/suspicious/noExplicitAny: Required for mixin pattern compatibility\n  export type LitMixin\u003cT = unknown\u003e = new (...args: any[]) =\u003e T \u0026 LitElement;\n}\n\nexport const tailwind = unsafeCSS(tailwindCss);\n\n// https://github.com/tailwindlabs/tailwindcss/issues/15005\n// Set all @property values from tailwind on the document\n// And only do this once (check if there is already a stylesheet with the same content)\nif (\n  tailwind.styleSheet \u0026\u0026\n  document?.adoptedStyleSheets \u0026\u0026\n  !document.adoptedStyleSheets.some(\n    (sheet) =\u003e\n      sheet.cssRules[0]?.cssText === tailwind.styleSheet?.cssRules[0].cssText\n  )\n) {\n  const propertiesSheet = new CSSStyleSheet();\n  let code = tailwind.cssText;\n  code = code\n    .replaceAll(\"inherits: false\", \"inherits: true\")\n    .substring(code.indexOf(\"@property\"));\n  propertiesSheet.replaceSync(code);\n  document.adoptedStyleSheets.push(propertiesSheet);\n}\n\nexport const TW = \u003cT extends LitMixin\u003e(superClass: T): T =\u003e\n  class extends superClass {\n    connectedCallback() {\n      super.connectedCallback();\n      if (this.shadowRoot) adoptStyles(this.shadowRoot, [tailwind]);\n    }\n  };\n```\n\n### tailwindMixin.d.ts\n\n```typescript\nimport { type LitElement } from \"lit\";\ndeclare global {\n  export type LitMixin\u003cT = unknown\u003e = new (...args: any[]) =\u003e T \u0026 LitElement;\n}\nexport declare const TW: \u003cT extends LitMixin\u003e(superClass: T) =\u003e T;\n```\n\n### tailwind.global.css\n\n```css\n@import \"tailwindcss\";\n```\n\n## 5. Using Tailwind CSS in your components\n\nWith the mixins in place, you can now import the `TW` mixin in the\n`my-element.ts` file and use it in the class definition and replace the\n`static styles` property with the tailwind classes.\n\n```diff\n import { LitElement, css, html } from \"lit\";\n import { customElement, property } from \"lit/decorators.js\";\n\n+import { TW } from \"../shared/tailwindMixin\";\n\n+const TwLitElement = TW(LitElement);\n\n\n @customElement(\"my-element\")\n+export class MyElement extends TwLitElement {\n  @property() docsHint = \"Click on the Vite and Lit logos to learn more\";\n  @property({ type: Number }) count = 0;\n\n  render() {\n    return html`\n+     \u003cdiv class=\"flex flex-col justify-center items-center gap-2 w-screen\"\u003e\n+       \u003cdiv class=\"flex gap-8\"\u003e\n          \u003ca href=\"https://vitejs.dev\" target=\"_blank\"\u003e\n+           \u003cimg src=${viteLogo} class=\"size-14\" alt=\"Vite logo\" /\u003e\n          \u003c/a\u003e\n          \u003ca href=\"https://lit.dev\" target=\"_blank\"\u003e\n+           \u003cimg src=${litLogo} class=\"size-14\" alt=\"Lit logo\" /\u003e\n          \u003c/a\u003e\n        \u003c/div\u003e\n        \u003cslot\u003e\u003c/slot\u003e\n+        \u003cdiv class=\"px-8\"\u003e\n          \u003cbutton\n+          class=\"rounded-lg w-full border border-purple-800 px-5 py-3 font-bold cursor-pointer\"\n            @click=${this._onClick}\n            part=\"button\"\n          \u003e\n            count is ${this.count}\n          \u003c/button\u003e\n        \u003c/div\u003e\n+       \u003cp class=\"text-gray-400\"\u003e${this.docsHint}\u003c/p\u003e\n+      \u003c/div\u003e\n    `;\n  }\n\n- static styles = css`\n\n  private _onClick() {\n    this.count++;\n  }\n }\n```\n\nNow you can run the project and see the tailwind styles applied to the\ncomponent.\n\n```bash\nbun run dev\n```\n\n## BONUS: make tailwind more useful\n\nUp to this point, you have successfully integrated Tailwind CSS with your Lit\nand Vite project. However, you can make it more useful by adding some utilities\nand extending the theme similar to how its done using shadcn/ui.\n\nFirst, install the required dependencies:\n\n```bash\nbun add class-variance-authority tailwind-merge clsx tw-animate-css\n```\n\nThen lets create a `utils.ts` file in the `lib/shared` directory and add the\nfollowing:\n\n```typescript\nimport { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n```\n\nNext we want to update out vscode settings to use Tailwind CSS IntelliSense and\nadd the classRegex to the settings.\n\n```json\n{\n  \"tailwindCSS.experimental.classRegex\": [\n    [\"cva\\\\(([^)]*)\\\\)\", \"[\\\"'`]([^\\\"'`]*).*?[\\\"'`]\"],\n    [\"cx\\\\(([^)]*)\\\\)\", \"(?:'|\\\"|`)([^']*)(?:'|\\\"|`)\"]\n  ]\n}\n```\n\nNow we can update the `tailwind.global.css` file to use the\n[pseudo-private properties](https://lea.verou.me/blog/2021/10/custom-properties-with-defaults/)\nthat can be overridden by user defined css variables:\n\n```diff\n@import \"tailwindcss\";\n+@import \"tw-animate-css\";\n+\n+@custom-variant dark (\u0026:is(.dark *));\n+@theme inline {\n+  --color-border: var(--_border);\n+  --color-input: var(--_input);\n+  --color-ring: var(--_ring);\n+  --color-background: var(--_background);\n+  --color-foreground: var(--_foreground);\n+\n+  --color-card: var(--_card);\n+  --color-card-foreground: var(--_card-foreground);\n+\n+  --color-popover: var(--_popover);\n+  --color-popover-foreground: var(--_popover-foreground);\n+\n+  --color-primary: var(--_primary);\n+  --color-primary-foreground: var(--_primary-foreground);\n+\n+  --color-secondary: var(--_secondary);\n+  --color-secondary-foreground: var(--_secondary-foreground);\n+\n+  --color-muted: var(--_muted);\n+  --color-muted-foreground: var(--_muted-foreground);\n+\n+  --color-accent: var(--_accent);\n+  --color-accent-foreground: var(--_accent-foreground);\n+\n+  --color-destructive: var(--_destructive);\n+  --color-destructive-foreground: var(--_destructive-foreground);\n+}\n+@layer base {\n+  :root,\n+  :host {\n+    --_background: var(--background, oklch(1 0 0));\n+    --_foreground: var(--foreground, oklch(0.147 0.004 49.25));\n+\n+    --_card: var(--card, oklch(1 0 0));\n+    --_card-foreground: var(--card-foreground, oklch(0.147 0.004 49.25));\n+\n+    --_popover: var(--popover, oklch(1 0 0));\n+    --_popover-foreground: var(--popover-foreground, oklch(0.147 0.004 49.25));\n+\n+    --_primary: var(--primary, oklch(0.216 0.006 56.043));\n+    --_primary-foreground: var(\n+      --primary-foreground,\n+      oklch(0.985 0.001 106.423)\n+    );\n+\n+    --_secondary: var(--secondary, oklch(0.97 0.001 106.424));\n+    --_secondary-foreground: var(\n+      --secondary-foreground,\n+      oklch(0.216 0.006 56.043)\n+    );\n+\n+    --_muted: var(--muted, oklch(0.97 0.001 106.424));\n+    --_muted-foreground: var(--muted-foreground, oklch(0.553 0.013 58.071));\n+\n+    --_accent: var(--accent, oklch(0.97 0.001 106.424));\n+    --_accent-foreground: var(--accent-foreground, oklch(0.216 0.006 56.043));\n+\n+    --_destructive: var(--destructive, oklch(0.577 0.245 27.325));\n+    --_destructive-foreground: var(\n+      --destructive-foreground,\n+      oklch(0.985 0.001 106.423)\n+    );\n+\n+    --_border: var(--border, oklch(0.923 0.003 48.717));\n+    --_input: var(--input, oklch(0.923 0.003 48.717));\n+    --_ring: var(--ring, oklch(0.709 0.01 56.259));\n+\n+    --_radius: var(--radius, 0.5rem);\n+  }\n+\n+  .dark,\n+  :host(.dark),\n+  :host-context(.dark) {\n+    --_background: var(--background, oklch(0.147 0.004 49.25));\n+    --_foreground: var(--foreground, oklch(0.985 0.001 106.423));\n+\n+    --_card: var(--card, oklch(0.216 0.006 56.043));\n+    --_card-foreground: var(--card-foreground, oklch(0.985 0.001 106.423));\n+\n+    --_popover: var(--popover, oklch(0.216 0.006 56.043));\n+    --_popover-foreground: var(\n+      --popover-foreground,\n+      oklch(0.985 0.001 106.423)\n+    );\n+\n+    --_primary: var(--primary, oklch(0.923 0.003 48.717));\n+    --_primary-foreground: var(--primary-foreground, oklch(0.216 0.006 56.043));\n+\n+    --_secondary: var(--secondary, oklch(0.268 0.007 34.298));\n+    --_secondary-foreground: var(\n+      --secondary-foreground,\n+      oklch(0.985 0.001 106.423)\n+    );\n+\n+    --_muted: var(--muted, oklch(0.268 0.007 34.298));\n+    --_muted-foreground: var(--muted-foreground, oklch(0.709 0.01 56.259));\n+\n+    --_accent: var(--accent, oklch(0.268 0.007 34.298));\n+    --_accent-foreground: var(--accent-foreground, oklch(0.985 0.001 106.423));\n+\n+    --_destructive: var(--destructive, oklch(0.704 0.191 22.216));\n+    --_destructive-foreground: var(\n+      --destructive-foreground,\n+      oklch(0.985 0.001 106.423)\n+    );\n+\n+    --_border: var(--border, oklch(1 0 0 / 10%));\n+    --_input: var(--input, oklch(1 0 0 / 15%));\n+    --_ring: var(--ring, oklch(0.553 0.013 58.071));\n+  }\n+}\n+\n+@layer base {\n+  * {\n+    @apply border-border outline-ring/50;\n+  }\n+  body {\n+    @apply bg-background text-foreground;\n+  }\n+}\n```\n\nThis setup allows you to use the `cva` function to apply variants that combine\ntailwind classes into more meaningful classes and then control the theme using\ncss variables.\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n  \u003chead\u003e\n    \u003cscript type=\"module\" src=\"/lib/main.ts\"\u003e\u003c/script\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003cstyle\u003e\n      :root {\n        --destructive: oklch(0.65 0.22 28);\n        --destructive-foreground: oklch(0.985 0.001 106.423);\n      }\n\n      .dark {\n        --destructive: oklch(0.72 0.19 28);\n        --destructive-foreground: oklch(0.985 0.001 106.423);\n      }\n    \u003c/style\u003e\n    \u003cmy-element variant=\"destructive\" size=\"lg\"\u003e\n      \u003ch1\u003eVite + Lit\u003c/h1\u003e\n    \u003c/my-element\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flloydrichards%2Fbase_lit-with-tailwind","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flloydrichards%2Fbase_lit-with-tailwind","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flloydrichards%2Fbase_lit-with-tailwind/lists"}