
Security News
Another Round of TEA Protocol Spam Floods npm, But It’s Not a Worm
Recent coverage mislabels the latest TEA protocol spam as a worm. Here’s what’s actually happening.
@jamsch/typed-tailwind-classes
Advanced tools
Typesafe tailwind classes for use in TypeScript projects
Typesafe tailwind classes for use in TypeScript projects
npm install --save @jamsch/typed-tailwind-classes
import type { TailwindClass } from '@jamsch/typed-tailwind-classes';
const blueBg: TailwindClass = 'bg-blue-500'; // OK
const extraBlueBg: TailwindClass = 'bg-blue-5000'; // Error!
Use the tw(...) helper to construct your class strings. One major difference with this helper (in comparison to classnames & clsx) is that modifiers (i.e. md:xxx, focus:xxx) are built using object syntax, as listed below:
import { tw } from '@jamsch/typed-tailwind-classes';
const condition = false;
const classes = tw(
'bg-blue-500',
'border-2',
// Conditions
condition && 'translate-x-10',
!condition && 'translate-y-10',
{
// Format: [class name]: [boolean]
'opacity-50': true,
'animate-bounce': false,
// Format: [modifier]: [class name]
hover: 'border-4',
'md:hover': 'opacity-100',
// Format: [modifier]: ([class name]|false|undefined)[]
'xs:hover': ['bg-blue-50', 'border-0', condition && 'animate-bounce'],
'md:dark:focus': ['bg-red-100', 'animate-pulse'],
},
);
console.log(classes);
// "bg-blue-500 border-2 translate-y-10 opacity-50 hover:border-4 md:hover:opacity-100 xs:hover:bg-blue-50 xs:hover:border-0 md:dark:focus:bg-red-100 md:dark:focus:animate-pulse"
In order to avoid purging state modifiers that use the tw(...) function (e.g. tw({ "md:hover": "bg-blue-50" })), you'll need to modify your Tailwind's purge config to add in a transformer function for the files that use tw(...).
// tailwind.config.js
// @ts-check
/**
* @param {string} content
* @returns {string}
*/
const transformer = (content) => {
// match content against the "tw(...)" function
const twFunctionRegex = /tw\(([^)]+)\)/g;
return content.replace(twFunctionRegex, (match, p1) => {
// flatten any "{ [key]: string[] }" in tw(...) including newlines and append the object keys to each item in the array
return (
p1
.replace(/\n/g, '')
// Match `[modifier]: ["item_one", "item_two"]`
.replace(/([^\s]+):\s*?\[([^\]]+)\]/g, (match, modifierKey, stringValues) => {
// modifierKey = "md" | `"md:hover"` | "`sm:focus`", etc
// remove all quotes from key
const modifier = modifierKey.replace(/["']/g, '');
// prepend modifier with each value in the array
// e.g. `"hello", "world"` => `"hover:hello", "hover:world"`
const modifierWithValue = stringValues.replace(/["']([^\s]+)["']/g, `"${modifier}:$1"`);
return modifierWithValue;
})
// Match `[Object key]: \"string_value\"`
.replace(/([^\s]+):\s*?"([^\"]+)"/g, (match, key, value) => {
const modifier = key.replace(/["']/g, '');
return `"${modifier}:${value}"`;
})
// Flatten the object
.replace(/[\{\}]/g, '')
);
});
};
// Install @types/tailwindcss for the below "@type" annotation to work correctly
/** @type {import("tailwindcss/tailwind-config").TailwindConfig} */
module.exports = {
mode: 'jit',
purge: {
// location to your files
content: ['./components/**/*.{tsx,ts}', './pages/**/*.{tsx,ts}'],
transform: {
tsx: transformer,
ts: transformer,
// NOTE: place any extra file extensions here
},
},
};
Since Tailwind allows you to add additional colors, screens and other properties, you need to provide this library the list of additions you've made. You can do this by importing the createTw class and providing a list of additional colors, screens and other options.
Currently, the list of supported config options are the following:
type ExtendConfig = {
animation?: string;
backgroundImage?: string;
colors?: string;
fontFamily?: string;
opacity?: string;
screens?: string;
};
// tw.ts
import { createTw } from '@jamsch/typed-tailwind-classes';
type ExtendedConfig = {
fontFamily: 'merriweather' | 'noto-sans';
colors: 'maroon' | 'navyblue';
animation: 'spin-360';
opacity: '15' | '35' | '65';
screens: 'portrait' | 'tablet';
};
export default createTw<ExtendedConfig>();
In another file:
import tw from './tw';
const classes = tw('font-noto-sans', 'bg-maroon', 'bg-opacity-15', 'opacity-35', {
portrait: 'border-0',
'portrait:hover': ['animate-spin-360', 'border-2'],
'tablet:dark': 'bg-navyblue',
}); // "font-noto-sans bg-maroon bg-opacity-15 opacity-35 portrait:border-0 portrait:hover:animate-spin-360 portrait:hover:border-2 tablet:dark:bg-navyblue"
Alternatively, you can use BuildTailwindClasses and BuildVariants to generate the class names.
import type { BuildTailwindClasses, BuildVariants } from '@jamsch/typed-tailwind-classes';
type ExtendedConfig = {
colors: 'maroon' | 'navyblue';
animation: 'spin-360';
opacity: '15' | '35' | '65';
screens: 'portrait' | 'tablet';
};
type ExtendedTailwindClasses = BuildTailwindClasses<ExtendedConfig>;
type Variants = BuildVariants<'portrait' | 'tablet'>;
// Warning: This will probably not work, and also isn't a great idea for performance
type AllClassesAndVariants = ExtendedTailwindClasses | `${Variants}:${ExtendedTailwindClasses}`;
const maroon: ExtendedTailwindClasses = 'bg-maroon'; // OK
const blue: ExtendedTailwindClasses = 'bg-blue-100'; // OK
const maroonTwo: ExtendedTailwindClasses = 'bg-maroon-2'; // Error!
FAQs
Typesafe tailwind classes for use in TypeScript projects
We found that @jamsch/typed-tailwind-classes demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Recent coverage mislabels the latest TEA protocol spam as a worm. Here’s what’s actually happening.

Security News
PyPI adds Trusted Publishing support for GitLab Self-Managed as adoption reaches 25% of uploads

Research
/Security News
A malicious Chrome extension posing as an Ethereum wallet steals seed phrases by encoding them into Sui transactions, enabling full wallet takeover.