Skip to content

Instantly share code, notes, and snippets.

@angelsantosa
Last active January 16, 2025 02:33
Show Gist options
  • Save angelsantosa/e475e5071c63e90d8a5b7cecd7164ec9 to your computer and use it in GitHub Desktop.
Save angelsantosa/e475e5071c63e90d8a5b7cecd7164ec9 to your computer and use it in GitHub Desktop.
tailwind based components from library

Let me help you understand the setup needed for sharing Tailwind-styled components across multiple applications:

When you have a shared library with Tailwind-styled components that you want to use in multiple applications, you'll need to:

  1. In your shared library:
  • Install Tailwind as a dependency
  • Configure Tailwind in your library's tailwind.config.js
  • Make sure your library's built CSS gets included in the package
  1. In both consuming applications:
  • Yes, you do need to install Tailwind in both applications
  • Configure Tailwind in each application's tailwind.config.js
  • Add your library's path to the content array in the Tailwind config

Here's an example setup:

// In your library's tailwind.config.js
module.exports = {
  content: [
    './src/**/*.{js,jsx,ts,tsx}',
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
// In each consuming application's tailwind.config.js
module.exports = {
  content: [
    './src/**/*.{js,jsx,ts,tsx}',
    // Add path to your library's components
    './node_modules/your-library/dist/**/*.{js,jsx,ts,tsx}'
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

The reason you need Tailwind installed in both applications is that:

  1. Tailwind needs to scan your code at build time to generate the CSS
  2. Each application might use different Tailwind classes outside your library
  3. Each application needs to process and purge unused styles

To ensure consistent styling:

  • Make sure all projects use the same Tailwind version
  • Consider using a shared Tailwind config that all projects extend
  • Be cautious with customizations in individual projects as they might affect the library's components

Would you like me to provide more specific configuration examples for your use case?

I'll help you create a complete example of a shared component library with Tailwind and how to extend its configuration.

First, let's set up the library:

// library/tailwind.config.js
module.exports = {
  prefix: 'lib-', // Optional: to avoid class name conflicts
  theme: {
    extend: {
      colors: {
        primary: {
          100: '#E6F7FF',
          500: '#1890FF',
          900: '#003A8C',
        },
      },
      spacing: {
        'custom': '32rem',
      }
    }
  }
}

Let's create a sample button component in your library:

// library/src/components/Button.tsx
import React from 'react';

export interface ButtonProps {
  variant?: 'primary' | 'secondary';
  children: React.ReactNode;
}

export const Button = ({ variant = 'primary', children }: ButtonProps) => {
  const baseClasses = 'lib-px-4 lib-py-2 lib-rounded-md lib-font-medium lib-transition-colors';
  const variantClasses = {
    primary: 'lib-bg-primary-500 lib-text-white hover:lib-bg-primary-900',
    secondary: 'lib-bg-primary-100 lib-text-primary-500 hover:lib-bg-primary-200'
  };

  return (
    <button className={`${baseClasses} ${variantClasses[variant]}`}>
      {children}
    </button>
  );
};

Now, in your consuming application, here's how you would extend the library's configuration:

// app/tailwind.config.js
const libraryConfig = require('your-library/tailwind.config.js');

module.exports = {
  content: [
    './src/**/*.{js,jsx,ts,tsx}',
    './node_modules/your-library/**/*.{js,jsx,ts,tsx}'
  ],
  theme: {
    extend: {
      ...libraryConfig.theme.extend,
      // Add app-specific extensions
      colors: {
        ...libraryConfig.theme.extend.colors,
        // Override or add new colors
        secondary: {
          500: '#722ED1',
        }
      }
    }
  }
}

To use the component in your application:

// app/src/App.tsx
import { Button } from 'your-library';

function App() {
  return (
    <div className="p-4">
      <Button variant="primary">Library Button</Button>
    </div>
  );
}

To make this work properly, you'll need to set up your library's package.json correctly:

// library/package.json
{
  "name": "your-library",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "files": [
    "dist",
    "tailwind.config.js"
  ],
  "peerDependencies": {
    "react": "^18.0.0",
    "tailwindcss": "^3.0.0"
  },
  "dependencies": {
    "classnames": "^2.3.1"
  },
  "devDependencies": {
    "tailwindcss": "^3.0.0",
    // ... other dev dependencies
  }
}

Key points to ensure styles work correctly:

  1. Export your library's Tailwind config:
// library/src/index.ts
export * from './components/Button';
export { default as tailwindConfig } from './tailwind.config';
  1. In your library's build process, make sure to:
  • Compile your TypeScript/JavaScript
  • Process your CSS with Tailwind
  • Include your Tailwind config in the distribution
  1. In your consuming application:
  • Install both your library and tailwindcss as dependencies
  • Import your library's components
  • Configure Tailwind to scan your library's components
  • Extend the library's Tailwind configuration

This setup ensures that:

  • Your library's styles are consistently applied across different applications
  • Applications can override or extend the library's styling if needed
  • The prefix ('lib-') prevents class name conflicts
  • Tailwind's purge process correctly identifies and includes the necessary styles

Would you like me to elaborate on any part of this setup or show how to handle more complex styling scenarios?

If you're using CSS custom properties (root variables) in your library, you'll need to handle them carefully to ensure they're available across all applications. Here's how to set it up:

In your library:

/* library/src/styles/base.css */
:root {
  --lib-primary-color: #1890FF;
  --lib-secondary-color: #722ED1;
  --lib-text-color: #2C3E50;
  --lib-spacing-sm: 0.5rem;
  --lib-spacing-md: 1rem;
  --lib-spacing-lg: 1.5rem;
}

/* You can also define dark mode variables */
@media (prefers-color-scheme: dark) {
  :root {
    --lib-primary-color: #40A9FF;
    --lib-text-color: #ECF0F1;
  }
}

Then use these variables in your Tailwind config:

// library/tailwind.config.js
module.exports = {
  prefix: 'lib-',
  theme: {
    extend: {
      colors: {
        primary: 'var(--lib-primary-color)',
        secondary: 'var(--lib-secondary-color)',
        text: 'var(--lib-text-color)',
      },
      spacing: {
        sm: 'var(--lib-spacing-sm)',
        md: 'var(--lib-spacing-md)',
        lg: 'var(--lib-spacing-lg)',
      }
    }
  }
}

In your component:

// library/src/components/Button.tsx
import React from 'react';
import '../styles/base.css'; // Import base styles

export const Button = ({ children }) => {
  return (
    <button className="lib-bg-primary lib-text-text lib-p-md">
      {children}
    </button>
  );
};

Make sure to export your base CSS:

// library/src/index.ts
export * from './components/Button';
export { default as tailwindConfig } from './tailwind.config';
import './styles/base.css';

In your consuming application, you can override these variables:

/* app/src/styles/variables.css */
:root {
  /* Override library variables */
  --lib-primary-color: #3498DB;
  
  /* Add application-specific variables */
  --app-accent-color: #E74C3C;
}

And in your application's Tailwind config:

// app/tailwind.config.js
const libraryConfig = require('your-library/tailwind.config.js');

module.exports = {
  content: [
    './src/**/*.{js,jsx,ts,tsx}',
    './node_modules/your-library/**/*.{js,jsx,ts,tsx}'
  ],
  theme: {
    extend: {
      ...libraryConfig.theme.extend,
      // Add app-specific colors that can use library variables
      colors: {
        ...libraryConfig.theme.extend.colors,
        accent: 'var(--app-accent-color)'
      }
    }
  }
}

Some important considerations:

  1. Prefix your library's CSS variables to avoid conflicts:
/* Good */
--lib-primary-color: #1890FF;

/* Could conflict with app variables */
--primary-color: #1890FF;
  1. Document your CSS variables:
/**
 * Library CSS Variables
 * --lib-primary-color: Main brand color
 * --lib-secondary-color: Secondary brand color
 * --lib-text-color: Default text color
 * --lib-spacing-sm: Small spacing unit (0.5rem)
 * --lib-spacing-md: Medium spacing unit (1rem)
 * --lib-spacing-lg: Large spacing unit (1.5rem)
 */
  1. Consider providing a CSS reset in your library:
/* library/src/styles/reset.css */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* Only if you want to enforce specific baseline styles */
  1. Make sure your build process handles CSS files:
// library/rollup.config.js or webpack.config.js
{
  plugins: [
    postcss({
      extract: 'styles.css',
      modules: false,
      use: ['sass'], // if using Sass
    })
  ]
}

This setup gives you the flexibility to:

  • Define default styles in your library
  • Allow applications to override these defaults
  • Keep styles consistent across applications
  • Maintain dark mode support
  • Add application-specific customizations

Would you like me to elaborate on any of these aspects or show how to handle more complex scenarios with CSS variables?

If you're using rollup (rslib) for your library, here's how to handle multiple components and their styles efficiently:

First, let's organize your library structure:

library/
├── src/
│   ├── components/
│   │   ├── Button/
│   │   │   ├── Button.tsx
│   │   │   └── button.css
│   │   ├── Card/
│   │   │   ├── Card.tsx
│   │   │   └── card.css
│   ├── styles/
│   │   ├── base.css
│   │   └── variables.css
│   └── index.ts
├── rollup.config.js
└── package.json

Your main entry file should export everything:

// src/index.ts
export * from './components/Button/Button';
export * from './components/Card/Card';
// Import all styles at the root level
import './styles/variables.css';
import './styles/base.css';

Configure rollup to handle CSS files:

// rollup.config.js
import typescript from '@rollup/plugin-typescript';
import postcss from 'rollup-plugin-postcss';
import autoprefixer from 'autoprefixer';
import tailwindcss from 'tailwindcss';

export default {
  input: 'src/index.ts',
  output: [
    {
      dir: 'dist',
      format: 'esm',
      preserveModules: true, // This keeps your file structure
      preserveModulesRoot: 'src'
    }
  ],
  plugins: [
    typescript(),
    postcss({
      plugins: [
        tailwindcss(),
        autoprefixer()
      ],
      extract: 'styles.css', // This will extract all CSS into one file
      minimize: true,
      modules: false // Set to true if you want CSS modules
    })
  ],
  external: ['react', 'react-dom'] // Add other external dependencies
}

Update your package.json:

{
  "name": "your-library",
  "version": "1.0.0",
  "main": "dist/index.js",
  "module": "dist/index.js",
  "types": "dist/index.d.ts",
  "files": [
    "dist",
    "tailwind.config.js"
  ],
  "style": "dist/styles.css",
  "sideEffects": [
    "**/*.css"
  ]
}

Then in your consuming application, you only need to import the library once:

// app/src/App.tsx
import 'your-library/dist/styles.css';
import { Button, Card } from 'your-library';

For Tailwind, make sure your library's tailwind.config.js is properly configured:

// library/tailwind.config.js
module.exports = {
  prefix: 'lib-',
  content: [
    './src/**/*.{js,jsx,ts,tsx}'
  ],
  theme: {
    extend: {
      // Your theme extensions
    }
  }
}

If you want to provide more granular control over styles, you could also export them separately:

// src/index.ts
export * from './components/Button/Button';
export * from './components/Card/Card';

// Export styles separately if needed
export { default as baseStyles } from './styles/base.css';
export { default as variables } from './styles/variables.css';

Key points to remember:

  1. Set sideEffects: ["**/*.css"] in package.json to prevent CSS from being tree-shaken
  2. Use preserveModules: true in rollup config to maintain your file structure
  3. Consider using CSS modules if you need scoped styles
  4. Make sure all your CSS files are processed by postcss and Tailwind
  5. Export your Tailwind config so consuming apps can extend it

Would you like me to explain any part in more detail or show how to handle specific scenarios with rslib?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment