نشر الإضافات وإعادة استخدامها
نظّم إضافة Tailwind كحزمة npm، واضبط خياراتها باستخدام غلاف دالي، وانشرها لإعادة استخدامها عبر مشاريع متعددة.
نشر الإضافات وإعادة استخدامها درس مجاني في Tailwind CSS Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Tailwind CSS Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Tailwind CSS Academy 4 دروس في المجموع.
بعض أجزاء هذا الدرس لم تُترجم بعد وتظهر باللغة الإنجليزية.
Why Publish a Tailwind Plugin?
Once you have written a useful Tailwind plugin — like a scrollbar utility, a text-shadow family, or a set of ARIA variants — other projects in your organization or the wider community can benefit from it. Publishing the plugin as an npm package makes it installable with a single command and versionable with semantic versioning. Teams stop copying plugin code between projects and instead share a single maintained artifact.
Structuring the Plugin Package
A Tailwind plugin npm package has a minimal structure. Create a directory with a package.json, a main entry file (typically index.js or src/index.js), and a README.md with usage instructions. The main file exports the plugin, which consumers add to their tailwind.config.js plugins array. That is the entire interface — there is no build step required for a simple plugin.
# Directory structure
tailwind-plugin-text-shadow/
index.js # plugin entry
package.json # npm metadata
README.md # usage docs
# package.json
{
'name': 'tailwind-plugin-text-shadow',
'version': '1.0.0',
'description': 'Tailwind CSS text-shadow utilities',
'main': 'index.js',
'keywords': ['tailwindcss', 'tailwind-plugin', 'text-shadow'],
'peerDependencies': {
'tailwindcss': '>=3.0.0'
}
}Writing the Plugin Entry File
The entry file exports the plugin using plugin.withOptions() so consumers can configure it. The outer function receives user options with defaults; the inner function receives Tailwind's plugin helpers. withOptions also allows consumers to pass the plugin without calling it as a function — Tailwind handles both usage patterns (require('plugin') and require('plugin')(options)).
// index.js
const plugin = require('tailwindcss/plugin');
module.exports = plugin.withOptions(function(options) {
return function({ matchUtilities, addUtilities, theme }) {
// Get user-defined theme values merged with defaults
const textShadowValues = theme('textShadow');
if (textShadowValues) {
matchUtilities(
{ 'text-shadow': (value) => ({ textShadow: value }) },
{ values: textShadowValues }
);
addUtilities({
'.text-shadow-none': { textShadow: 'none' }
});
}
};
},
// Second argument: default theme extension
function(options) {
return {
theme: {
textShadow: {
sm: '0 1px 2px rgba(0,0,0,0.2)',
DEFAULT: '0 2px 4px rgba(0,0,0,0.3)',
lg: '0 4px 8px rgba(0,0,0,0.4)'
}
}
};
});Consumer Usage Pattern
When a consumer installs your published plugin, they add it to their plugins array. Thanks to plugin.withOptions, they can use it either with or without configuration. The plugin's default theme extension adds its utility values to the consumer's Tailwind config automatically, so the utilities appear in editor autocomplete without any manual theme configuration.
# Consumer install
npm install tailwind-plugin-text-shadow
// tailwind.config.js
module.exports = {
content: ['./src/**/*.{html,js}'],
theme: { extend: {} },
plugins: [
// Without options — uses plugin defaults
require('tailwind-plugin-text-shadow'),
// With custom options
require('tailwind-plugin-text-shadow')({
// options if your plugin accepts any
})
]
};
// Consumer can also extend the theme:
theme: {
extend: {
textShadow: {
xl: '0 8px 16px rgba(0,0,0,0.5)' // adds to plugin defaults
}
}
}Semantic Versioning for Plugins
Apply semantic versioning to your plugin package. A patch version (1.0.0 → 1.0.1) fixes bugs without changing the API or generated classes. A minor version (1.0.0 → 1.1.0) adds new utilities, variants, or options without removing anything. A major version (1.0.0 → 2.0.0) makes breaking changes — like renaming a utility, removing an option, or changing default values in a way that affects generated CSS.
# Publishing workflow
# 1. Update version in package.json
# Patch: npm version patch
# Minor: npm version minor
# Major: npm version major
# 2. Update CHANGELOG.md
# ## v1.1.0 — 2026-06-21
# ### Added
# - text-shadow-xl value for larger displays
# - Arbitrary value support via matchUtilities
# 3. Publish to npm
npm publish
# 4. Tag the release in git
git push --tagsWriting Tests for Your Plugin
Test your plugin by rendering it with Tailwind and asserting the generated CSS. The @tailwindcss/jest approach uses PostCSS directly. Alternatively, create HTML fixture files, run the Tailwind CLI, and snapshot the output CSS. Tests catch regressions when you update the plugin — ensuring that text-shadow-lg still generates the correct text-shadow property after refactoring.
// test/plugin.test.js
const postcss = require('postcss');
const tailwindcss = require('tailwindcss');
const textShadowPlugin = require('../index');
async function generateCSS(html) {
const result = await postcss([
tailwindcss({
content: [{ raw: html }],
plugins: [textShadowPlugin]
})
]).process('@tailwind utilities', { from: undefined });
return result.css;
}
test('generates text-shadow-sm utility', async () => {
const css = await generateCSS('<div class='text-shadow-sm'>');
expect(css).toContain('text-shadow');
expect(css).toContain('rgba(0,0,0,0.2)');
});
test('generates text-shadow-none utility', async () => {
const css = await generateCSS('<div class='text-shadow-none'>');
expect(css).toContain('text-shadow: none');
});Writing a README for Your Plugin
A clear README dramatically increases the adoption of your plugin. Include: an installation command, a minimal quickstart showing how to add it to tailwind.config.js, a table of all generated utility classes with their CSS output, configuration options if any, and browser compatibility notes for utilities using newer CSS features. Examples with both code and visual output are especially helpful.
# tailwind-plugin-text-shadow
Adds text-shadow utilities to Tailwind CSS.
## Installation
npm install tailwind-plugin-text-shadow
## Setup
// tailwind.config.js
plugins: [require('tailwind-plugin-text-shadow')]
## Available Classes
| Class | CSS Output |
|-------------------|-------------------------------------|
| text-shadow-sm | text-shadow: 0 1px 2px rgba(...) |
| text-shadow | text-shadow: 0 2px 4px rgba(...) |
| text-shadow-lg | text-shadow: 0 4px 8px rgba(...) |
| text-shadow-none | text-shadow: none |
## Arbitrary Values
text-shadow-[0_4px_8px_rgba(0,0,0,0.5)]Sharing Within a Monorepo
In a monorepo, you can share a Tailwind plugin across packages without publishing to npm. Create a packages/tailwind-plugins workspace package and reference it via the workspace protocol in other packages. The plugin is used identically to a published package but resolves from your local filesystem, enabling rapid iteration without a publish cycle.
# monorepo structure
packages/
tailwind-plugins/
index.js
package.json ('name': '@myapp/tailwind-plugins')
web/
package.json
tailwind.config.js
admin/
package.json
tailwind.config.js
# In web/package.json
{
'devDependencies': {
'@myapp/tailwind-plugins': 'workspace:*'
}
}
# In web/tailwind.config.js
plugins: [
require('@myapp/tailwind-plugins')
]Peer Dependencies and Compatibility
Declare tailwindcss as a peer dependency (not a regular dependency) in your plugin's package.json. This prevents multiple versions of Tailwind from being installed when consumers install your plugin — the consumer's own Tailwind installation is used. Specify a wide range like >=3.0.0 unless your plugin uses APIs only available in specific versions.
// package.json
{
'name': 'tailwind-plugin-text-shadow',
'version': '1.2.0',
'main': 'index.js',
'peerDependencies': {
'tailwindcss': '>=3.0.0'
},
'devDependencies': {
// Only for testing — not shipped to consumers
'tailwindcss': '^3.4.0',
'jest': '^29.0.0',
'postcss': '^8.0.0'
},
'scripts': {
'test': 'jest',
'build': 'echo no build needed'
}
}Community Plugin Ecosystem
The Tailwind plugin ecosystem includes many high-quality community plugins. Before building your own, search npm for tailwindcss-plugin-* or check the awesome-tailwindcss repository on GitHub. Notable community plugins include tailwindcss-animate for extended animation utilities, tailwindcss-radix for Radix UI data-attribute variants, and tailwind-scrollbar for custom scrollbar styling. Contributing to these projects is often more impactful than maintaining a fork.
# Popular community plugins
npm install tailwindcss-animate # rich animation utilities
npm install tailwindcss-radix # Radix UI data-* variants
npm install tailwind-scrollbar # cross-browser scrollbar styling
npm install @headlessui/tailwindcss # Headless UI state variants
npm install tailwindcss-bg-patterns # background pattern utilities
# Usage:
plugins: [
require('tailwindcss-animate'),
require('tailwindcss-radix')(),
require('@headlessui/tailwindcss')
]Plugin Deprecation and Maintenance
Maintaining a published plugin is a commitment. When Tailwind releases new built-in utilities that overlap with your plugin, announce a deprecation timeline in your README and CHANGELOG. Publish a final version that prints a deprecation warning and points users to the built-in alternative. After the deprecation period, archive the repository. This communicates professionalism and helps your users migrate without being stranded on an unmaintained dependency.
// index.js — deprecation notice
module.exports = plugin.withOptions(function() {
return function({ addUtilities }) {
if (process.env.NODE_ENV !== 'production') {
console.warn(
'[tailwind-plugin-text-shadow] DEPRECATED: ' +
'Tailwind CSS v4 now includes text-shadow utilities. ' +
'See: https://tailwindcss.com/docs/text-shadow. ' +
'This plugin will be archived on 2027-01-01.'
);
}
// Plugin still works during deprecation period
addUtilities({ '.text-shadow-sm': { textShadow: '...' } });
};
});Quick Check
Test your understanding of Tailwind CSS Mastery concepts from this lesson.
Lesson Recap
In this lesson you learned: plugin.withOptions makes plugins configurable and compatible with both call and no-call usage patterns, peerDependencies prevent duplicate Tailwind installs in consumer projects, and workspace protocols enable plugin sharing within monorepos without publishing. Next up we shift to accessibility — starting with color contrast and readable text.
الأسئلة الشائعة
هل درس «نشر الإضافات وإعادة استخدامها» مجاني؟
نعم — نص درس «نشر الإضافات وإعادة استخدامها» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Tailwind CSS Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Tailwind CSS Academy 4 دروس في المجموع.
ماذا ستتعلم في «نشر الإضافات وإعادة استخدامها»؟
نظّم إضافة Tailwind كحزمة npm، واضبط خياراتها باستخدام غلاف دالي، وانشرها لإعادة استخدامها عبر مشاريع متعددة. تتمرن على Tailwind CSS Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Tailwind CSS Academy؟
لا تُشترط خبرة سابقة. Tailwind CSS Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.
كم من الوقت يستغرق درس «نشر الإضافات وإعادة استخدامها»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Tailwind CSS Academy هذا؟
نعم. كل درس في Tailwind CSS Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- واجهة Tailwind الإضافية
- إضافة أدوات مساعدة مخصصة عبر إضافة
- إضافة تنويعات مخصصة عبر إضافة
- نشر الإضافات وإعادة استخدامها