Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions example/src/common/Pattern.svelte
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
<script lang="ts">
import HtmlCode from './HtmlCode.svelte';

export interface IPatternConfig {
name: string;
description: string;
}
export interface IPattern {
name: string;
class: string;
configClasses: { name: string; description: string }[];
configClasses: IPatternConfig[];
}

interface IProps {
Expand All @@ -20,10 +24,10 @@
<h2 class="mb-4 text-3xl font-medium">{name}</h2>

<h3 class="font-medium text-white/90 ~2xs/xs:~text-base/lg">Configuration classes:</h3>
<ul class="ml-2 mb-4 ~2xs/xs:~text-sm/base grid content-start">
<ul class="ml-2 mb-4 ~2xs/xs:~text-sm/base grid content-start gap-1">
{#each configClasses as config}
<li class="flex max-xl:flex-col">
<span>-<code class="ml-1 font-medium text-class">{config.name}</code></span>
<code class="ml-1 font-medium text-class">.{config.name}</code>
<p class="ml-3 text-white/75">{config.description}</p>
</li>
{/each}
Expand Down
56 changes: 45 additions & 11 deletions example/src/routes/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,29 +1,63 @@
<script lang="ts">
import Pattern, { type IPattern } from '$common/Pattern.svelte';
import Pattern, { type IPattern, type IPatternConfig } from '$common/Pattern.svelte';
import Header from '$modules/Header.svelte';
import githubCodeStyle from 'svelte-highlight/styles/github-dark';

const LINE_CONFIGS: IPatternConfig[] = [
{ name: 'bg-pattern-line-2', description: 'Configure width of pattern lines' },
{
name: 'bg-pattern-line-[48]',
description: 'Configure width of pattern lines (custom value, without unit)'
},
{ name: 'bg-pattern-line-red-500', description: 'Configure color of pattern lines' },
{
name: 'bg-pattern-line-[#a8a8a8]',
description: 'Configure color of pattern lines (custom value)'
}
];

const SPACING_CONFIGS: IPatternConfig[] = [
{ name: 'bg-pattern-spacing-48', description: 'Configure the spacing between lines' },
{
name: 'bg-pattern-spacing-[225]',
description: 'Configure the spacing between lines (custom value, without unit)'
}
];

const HATCHING_DIRECTION_CONFIGS: IPatternConfig[] = [
{
name: 'bg-pattern-hatching-left-to-right',
description: 'Configure hatching to start from top left'
},
{
name: 'bg-pattern-hatching-right-to-left',
description: 'Configure hatching to start from top right'
}
];

const PATTERNS: IPattern[] = [
{
name: 'Grid',
configClasses: [
{ name: 'bg-pattern-line-2', description: 'Configure width of pattern lines' },
{
name: 'bg-pattern-line-[48]',
description: 'Configure width of pattern lines (custom value, must not contain unit)'
},
{ name: 'bg-pattern-line-red-500', description: 'Configure color of pattern lines' },
{
name: 'bg-pattern-line-[#a8a8a8]',
description: 'Configure color of pattern lines (custom value)'
},
...LINE_CONFIGS,
{ name: 'bg-pattern-cell-48', description: 'Configure size of grid pattern cells' },
{
name: 'bg-pattern-cell-[225]',
description: 'Configure size of grid pattern cells (custom value)'
}
],
class: 'bg-blue-500 bg-pattern-grid bg-pattern-line-0.5 bg-pattern-cell-32'
},
{
name: 'Hatching',
configClasses: [...LINE_CONFIGS, ...SPACING_CONFIGS, ...HATCHING_DIRECTION_CONFIGS],
class:
'bg-blue-500 bg-pattern-hatching bg-pattern-line-0.5 bg-pattern-spacing-16 bg-pattern-hatching-left-to-right'
},
{
name: 'Cross-Hatching',
configClasses: [...LINE_CONFIGS, ...SPACING_CONFIGS, ...HATCHING_DIRECTION_CONFIGS],
class: 'bg-blue-500 bg-pattern-cross-hatching bg-pattern-line-0.5 bg-pattern-spacing-16'
}
];
</script>
Expand Down
1 change: 1 addition & 0 deletions src/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const LINE_WIDTHS = [
0.5, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16,
];
export const GRID_CELL_SIZE: number[] = [...Array(128)].map((_, i) => i + 1);
export const SPACING: number[] = [...Array(128)].map((_, i) => i + 1);
export const OFFSETS: number[] = [...Array(128 + 16)].map((_, i) => i + 1);

export const DEFAULT_OPTS: IResolvedOpts = {
Expand Down
17 changes: 15 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import plugin from "tailwindcss/plugin";
import { GRID_CELL_SIZE, LINE_WIDTHS, OFFSETS } from "./consts";
import { GRID_CELL_SIZE, LINE_WIDTHS, OFFSETS, SPACING } from "./consts";
import { arrToTwConfig } from "./lib/arrToTwConfig";
import { generateCellSizes, matchCellSize } from "./lib/cellSizes";
import {
generateLineColors,
Expand All @@ -8,25 +9,36 @@ import {
} from "./lib/line";
import { generateOffsets, matchOffsets } from "./lib/offsets";
import { resolveOptions } from "./lib/resolveOptions";
import { generateSpacing, matchSpacing } from "./lib/spacing";
import { generateGridClass } from "./patterns/grid";
import { generateHatchingClass, genreateHatchingDirection } from "./patterns/hatching";
import type { IOptions } from "./types";
import { arrToTwConfig } from "./lib/arrToTwConfig";

export default plugin.withOptions<IOptions | undefined>(
(options) => (api) => {
const { addUtilities, matchUtilities, e } = api;
const opts = resolveOptions(options);

addUtilities([
// Patterns
generateGridClass(e(`${opts.prefix}pattern-grid`)),
generateHatchingClass(e(`${opts.prefix}pattern-hatching`)),
generateHatchingClass(e(`${opts.prefix}pattern-cross-hatching`), {
isCrossHatch: true,
}),

// Configs
generateLineWidths(api, opts),
generateCellSizes(api, opts),
generateSpacing(api, opts),
genreateHatchingDirection(api, opts),
generateLineColors(api, opts),
generateOffsets(api, opts),
]);

matchUtilities({
...matchCellSize(api, opts),
...matchSpacing(api, opts),
...matchLineWidthsAndColors(api, opts),
...matchOffsets(api, opts),
});
Expand All @@ -35,6 +47,7 @@ export default plugin.withOptions<IOptions | undefined>(
theme: {
bgPatternLineWidth: arrToTwConfig(LINE_WIDTHS),
bgPatternCellSize: arrToTwConfig(GRID_CELL_SIZE),
bgPatternSpacing: arrToTwConfig(SPACING),
bgPatternOffsets: arrToTwConfig(OFFSETS, "px"),
},
}),
Expand Down
27 changes: 27 additions & 0 deletions src/lib/spacing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { CSSRuleObject, PluginAPI } from "tailwindcss/types/config";
import type { IOptions } from "../types";

export const generateSpacing = (
api: PluginAPI,
opts: IOptions,
): CSSRuleObject => {
const spacing = api.theme("bgPatternSpacing");
const styles: Record<string, unknown> = {};
if (spacing) {
for (const key in spacing) {
const value = spacing[key];
styles[`.${api.e(`${opts.prefix}pattern-spacing-${key}`)}`] = {
"--tw-spacing": `${value} /* px */`,
} as unknown as CSSRuleObject;
}
}
return styles as CSSRuleObject;
};

export const matchSpacing = (api: PluginAPI, opts: IOptions) => ({
[api.e(`${opts.prefix}pattern-spacing`)]: (value: string) => {
return {
"--tw-spacing": `${value} /* px */`,
} as unknown as CSSRuleObject;
}
});
102 changes: 102 additions & 0 deletions src/patterns/hatching.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import type { CSSRuleObject, PluginAPI } from "tailwindcss/types/config";
import type { IOptions } from "../types";

interface IOpts {
lineSize: number;
spacing: number;
offsetX: number;
offsetY: number;
isCrossHatch: boolean;
isRightLeaning: boolean;
lineColor: string;
}

export const generateHatchingClass = (
className: string,
_opts?: Partial<IOpts>,
): CSSRuleObject => {
const opts: IOpts = {
lineSize: 1,
lineColor: "#ffffff",
spacing: 12,
offsetX: 0,
offsetY: 0,
isCrossHatch: false,
isRightLeaning: false,
..._opts,
};

let bgImageCode = `linear-gradient(
var(--tw-hatching-angle),
transparent var(--tw-1st-start-stop),
var(--tw-line-color) var(--tw-1st-start-stop),
var(--tw-line-color) var(--tw-1st-end-stop),
transparent var(--tw-1st-end-stop),
transparent 100%
),
linear-gradient(
var(--tw-hatching-angle),
transparent var(--tw-2ed-start-stop),
var(--tw-line-color) var(--tw-2ed-start-stop),
var(--tw-line-color) var(--tw-2ed-end-stop),
transparent var(--tw-2ed-end-stop),
transparent 100%
)
`;

if (opts.isCrossHatch) {
bgImageCode += `, linear-gradient(
calc(var(--tw-hatching-angle) * -1),
transparent var(--tw-1st-start-stop),
var(--tw-line-color) var(--tw-1st-start-stop),
var(--tw-line-color) var(--tw-1st-end-stop),
transparent var(--tw-1st-end-stop),
transparent 100%
),
linear-gradient(
calc(var(--tw-hatching-angle) * -1),
transparent var(--tw-2ed-start-stop),
var(--tw-line-color) var(--tw-2ed-start-stop),
var(--tw-line-color) var(--tw-2ed-end-stop),
transparent var(--tw-2ed-end-stop),
transparent 100%
)`;
}

return {
[`.${className}`]: {
"--tw-line-size": opts.lineSize.toString(),
"--tw-spacing": opts.spacing.toString(),
"--tw-offset-x": `${opts.offsetX * -1}px`,
"--tw-offset-y": `${opts.offsetY * -1}px`,
"--tw-hatching-angle": `${opts.isRightLeaning ? -45 : 45}deg`,
"--tw-line-color": opts.lineColor,

"--tw-unit": "calc((var(--tw-line-size) + var(--tw-spacing)) * 2)",
"--tw-line-stop":
"calc((var(--tw-line-size) / var(--tw-unit) * 100%) / 2)",
"--tw-1st-start-stop": "calc(75% - var(--tw-line-stop))",
"--tw-1st-end-stop": "calc(75% + var(--tw-line-stop))",
"--tw-2ed-start-stop": "calc(25% - var(--tw-line-stop))",
"--tw-2ed-end-stop": "calc(25% + var(--tw-line-stop))",
backgroundImage: bgImageCode,
backgroundSize: "calc(var(--tw-unit) * 1px) calc(var(--tw-unit) * 1px)",
backgroundPosition:
"calc(var(--tw-spacing) * -0.5px + var(--tw-offset-x)) var(--tw-offset-y)",
},
};
};

export const genreateHatchingDirection = (
api: PluginAPI,
opts: IOptions,
): CSSRuleObject => {
return {
[`.${api.e(`${opts.prefix}pattern-hatching-left-to-right`)}`]: {
"--tw-hatching-angle": "45deg",
},
[`.${api.e(`${opts.prefix}pattern-hatching-right-to-left`)}`]: {
"--tw-hatching-angle": "-45deg",
},
} as unknown as CSSRuleObject;
};
27 changes: 26 additions & 1 deletion tests/index.test.ts → tests/config.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, test } from "vitest";
import { DEFAULT_OPTS, LINE_WIDTHS, OFFSETS } from "../src/consts";
import { DEFAULT_OPTS, LINE_WIDTHS, OFFSETS, SPACING } from "../src/consts";
import { css, expectCssToBe, generateTwCss } from "./utils";

describe("line widths", () => {
Expand All @@ -24,6 +24,31 @@ describe("line widths", () => {
});
});

describe("spacing", () => {
test(`spacing ${SPACING[0]}-${SPACING[SPACING.length - 1]}`, async () => {
const expected: string[] = [];
const classes: string[] = [];
for (const value of SPACING) {
classes.push(`${DEFAULT_OPTS.prefix}pattern-spacing-${value}`);
expected.push(
css`.${DEFAULT_OPTS.prefix}pattern-spacing-${value} {
--tw-spacing: ${value} /* px */
}`,
);
}
expectCssToBe(await generateTwCss(classes.join(" ")), expected.join(""));
});

test("custom spacing", async () => {
expectCssToBe(
await generateTwCss(`${DEFAULT_OPTS.prefix}pattern-spacing-[321]`),
css`.${DEFAULT_OPTS.prefix}pattern-spacing-[321] {
--tw-spacing: 321 /* px */
}`,
);
});
});

describe("offsets", () => {
test(`offsets ${OFFSETS[0]}-${OFFSETS[OFFSETS.length - 1]}`, async () => {
const expected: string[] = [];
Expand Down
5 changes: 5 additions & 0 deletions tests/consts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
GRID_CELL_SIZE,
LINE_WIDTHS,
OFFSETS,
SPACING,
} from "../src/consts";

describe("default production values", () => {
Expand All @@ -25,4 +26,8 @@ describe("default production values", () => {
test("offsets", () => {
expect(OFFSETS).toEqual([...Array(128 + 16)].map((_, i) => i + 1));
});

test("spacing", () => {
expect(SPACING).toEqual([...Array(128)].map((_, i) => i + 1));
});
});
Loading