├── assets
└── icon.png
├── postcss.config.js
├── src
├── features
│ └── count-button.tsx
├── popup.tsx
├── lib
│ └── utils.ts
├── content.tsx
├── components
│ └── ui
│ │ ├── label.tsx
│ │ └── button.tsx
└── style.css
├── tsconfig.json
├── components.json
├── .gitignore
├── .prettierrc.mjs
├── .editorconfig
├── .github
└── workflows
│ └── submit.yml
├── LICENSE
├── package.json
├── README.md
└── tailwind.config.js
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sugarforever/plasmo-tailwind-shadcn-boilerplate/main/assets/icon.png
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @type {import('postcss').ProcessOptions}
3 | */
4 | module.exports = {
5 | plugins: {
6 | tailwindcss: {}
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/features/count-button.tsx:
--------------------------------------------------------------------------------
1 | import { useReducer } from "react"
2 | import { Button } from "@/components/ui/button"
3 |
4 | export const CountButton = (props: { id: string }) => {
5 | const [count, increase] = useReducer((c) => c + 1, 0)
6 |
7 | return (
8 |
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "plasmo/templates/tsconfig.base",
3 | "exclude": [
4 | "node_modules"
5 | ],
6 | "include": [
7 | ".plasmo/index.d.ts",
8 | "./**/*.ts",
9 | "./**/*.tsx"
10 | ],
11 | "compilerOptions": {
12 | "paths": {
13 | "~*": [
14 | "./src/*"
15 | ],
16 | "@/*": ["./src/*"]
17 | },
18 | "baseUrl": "."
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/popup.tsx:
--------------------------------------------------------------------------------
1 | import { CountButton } from "~features/count-button"
2 | import { Label } from "@/components/ui/label"
3 |
4 | import "~style.css"
5 |
6 | function IndexPopup() {
7 | return (
8 |
9 |
10 |
11 |
12 | )
13 | }
14 |
15 | export default IndexPopup
16 |
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "new-york",
4 | "rsc": false,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "tailwind.config.js",
8 | "css": "src/index.css",
9 | "baseColor": "zinc",
10 | "cssVariables": true,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "@/components",
15 | "utils": "@/lib/utils",
16 | "ui": "@/components/ui",
17 | "lib": "@/lib",
18 | "hooks": "@/hooks"
19 | },
20 | "iconLibrary": "lucide"
21 | }
22 |
--------------------------------------------------------------------------------
/src/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { clsx, type ClassValue } from "clsx"
2 | import { twMerge } from "tailwind-merge"
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs))
6 | }
7 |
8 | export function formatDate(input: string | number): string {
9 | const date = new Date(input)
10 | return date.toLocaleDateString("en-US", {
11 | month: "long",
12 | day: "numeric",
13 | year: "numeric",
14 | })
15 | }
16 |
17 | export function absoluteUrl(path: string) {
18 | return `${process.env.NEXT_PUBLIC_APP_URL}${path}`
19 | }
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
3 |
4 | # dependencies
5 | /node_modules
6 | /.pnp
7 | .pnp.js
8 |
9 | # testing
10 | /coverage
11 |
12 | #cache
13 | .turbo
14 |
15 | # misc
16 | .DS_Store
17 | *.pem
18 |
19 | # debug
20 | npm-debug.log*
21 | yarn-debug.log*
22 | yarn-error.log*
23 | .pnpm-debug.log*
24 |
25 | # local env files
26 | .env*
27 |
28 | out/
29 | build/
30 | dist/
31 |
32 | # plasmo - https://www.plasmo.com
33 | .plasmo
34 |
35 | # bpp - http://bpp.browser.market/
36 | keys.json
37 |
38 | # typescript
39 | .tsbuildinfo
40 |
--------------------------------------------------------------------------------
/src/content.tsx:
--------------------------------------------------------------------------------
1 | import cssText from "data-text:~style.css"
2 | import type { PlasmoCSConfig } from "plasmo"
3 |
4 | import { CountButton } from "~features/count-button"
5 |
6 | export const config: PlasmoCSConfig = {
7 | matches: ["https://www.plasmo.com/*"]
8 | }
9 |
10 | export const getStyle = () => {
11 | const style = document.createElement("style")
12 | style.textContent = cssText
13 | return style
14 | }
15 |
16 | const PlasmoOverlay = () => {
17 | return (
18 |
19 |
20 |
21 | )
22 | }
23 |
24 | export default PlasmoOverlay
25 |
--------------------------------------------------------------------------------
/.prettierrc.mjs:
--------------------------------------------------------------------------------
1 | /**
2 | * @type {import('prettier').Options}
3 | */
4 | export default {
5 | printWidth: 80,
6 | tabWidth: 2,
7 | useTabs: false,
8 | semi: false,
9 | singleQuote: false,
10 | trailingComma: "none",
11 | bracketSpacing: true,
12 | bracketSameLine: true,
13 | plugins: ["@ianvs/prettier-plugin-sort-imports"],
14 | importOrder: [
15 | "", // Node.js built-in modules
16 | "", // Imports not matched by other special words or groups.
17 | "", // Empty line
18 | "^@plasmo/(.*)$",
19 | "",
20 | "^@plasmohq/(.*)$",
21 | "",
22 | "^~(.*)$",
23 | "",
24 | "^[./]"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/ui/label.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import * as LabelPrimitive from "@radix-ui/react-label"
3 | import { cva, type VariantProps } from "class-variance-authority"
4 |
5 | import { cn } from "@/lib/utils"
6 |
7 | const labelVariants = cva(
8 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
9 | )
10 |
11 | const Label = React.forwardRef<
12 | React.ElementRef,
13 | React.ComponentPropsWithoutRef &
14 | VariantProps
15 | >(({ className, ...props }, ref) => (
16 |
21 | ))
22 | Label.displayName = LabelPrimitive.Root.displayName
23 |
24 | export { Label }
25 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: https://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # Unix-style newlines with a newline ending every file
7 | [*]
8 | end_of_line = lf
9 | insert_final_newline = true
10 | charset = utf-8
11 | trim_trailing_whitespace = true
12 |
13 | # TypeScript and JavaScript files
14 | [*.{ts,tsx,js,jsx}]
15 | indent_style = space
16 | indent_size = 2
17 |
18 | # CSS, SCSS, and JSON files
19 | [*.{css,scss,json}]
20 | indent_style = space
21 | indent_size = 2
22 |
23 | # HTML files
24 | [*.html]
25 | indent_style = space
26 | indent_size = 2
27 |
28 | # Markdown files
29 | [*.md]
30 | trim_trailing_whitespace = false
31 | max_line_length = off
32 |
33 | # Package files
34 | [package.json]
35 | indent_style = space
36 | indent_size = 2
37 |
38 | # Config files
39 | [*.{yml,yaml}]
40 | indent_style = space
41 | indent_size = 2
--------------------------------------------------------------------------------
/.github/workflows/submit.yml:
--------------------------------------------------------------------------------
1 | name: "Submit to Web Store"
2 | on:
3 | workflow_dispatch:
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v3
10 | - name: Cache pnpm modules
11 | uses: actions/cache@v3
12 | with:
13 | path: ~/.pnpm-store
14 | key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
15 | restore-keys: |
16 | ${{ runner.os }}-
17 | - uses: pnpm/action-setup@v2.2.4
18 | with:
19 | version: latest
20 | run_install: true
21 | - name: Use Node.js 16.x
22 | uses: actions/setup-node@v3.4.1
23 | with:
24 | node-version: 16.x
25 | cache: "pnpm"
26 | - name: Build the extension
27 | run: pnpm build
28 | - name: Package the extension into a zip artifact
29 | run: pnpm package
30 | - name: Browser Platform Publish
31 | uses: PlasmoHQ/bpp@v3
32 | with:
33 | keys: ${{ secrets.SUBMIT_KEYS }}
34 | artifact: build/chrome-mv3-prod.zip
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 sugarforever
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.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "plasmo-tailwind-shadcn-boilerplate",
3 | "displayName": "Plasmo tailwind shadcn boilerplate",
4 | "version": "0.0.1",
5 | "description": "A basic Plasmo extension.",
6 | "author": "sugarforever ,
39 | VariantProps {
40 | asChild?: boolean
41 | }
42 |
43 | const Button = React.forwardRef(
44 | ({ className, variant, size, asChild = false, ...props }, ref) => {
45 | const Comp = asChild ? Slot : "button"
46 | return (
47 |
52 | )
53 | }
54 | )
55 | Button.displayName = "Button"
56 |
57 | export { Button, buttonVariants }
58 |
--------------------------------------------------------------------------------