Skip to content

Commit 9e68777

Browse files
committed
feat: add directional border color support
Add support for per-side border colors (border-t-red-500, border-l-blue-500, etc.) and horizontal/vertical shortcuts (border-x-*, border-y-*). - Extend parseColor to handle border-{t|r|b|l|x|y}-{color} patterns - Modify parseBorder to coordinate with parseColor for color detection - Pass customColors to parseBorder for proper color pattern matching - Support all color features: opacity modifiers, arbitrary values, custom colors Border-x and border-y expand to multiple React Native properties since borderHorizontalColor/borderVerticalColor don't exist.
1 parent 330ad1d commit 9e68777

File tree

3 files changed

+58
-5
lines changed

3 files changed

+58
-5
lines changed

src/parser/borders.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/
44

55
import type { StyleObject } from "../types";
6+
import { parseColor } from "./colors";
67

78
// Border width scale
89
export const BORDER_WIDTH_SCALE: Record<string, number> = {
@@ -108,16 +109,18 @@ function parseArbitraryBorderRadius(value: string): number | null {
108109

109110
/**
110111
* Parse border classes
112+
* @param cls - The class name to parse
113+
* @param customColors - Optional custom colors from tailwind.config (used to detect color patterns)
111114
*/
112-
export function parseBorder(cls: string): StyleObject | null {
115+
export function parseBorder(cls: string, customColors?: Record<string, string>): StyleObject | null {
113116
// Border style (must come before parseBorderWidth)
114117
if (cls === "border-solid") return { borderStyle: "solid" };
115118
if (cls === "border-dotted") return { borderStyle: "dotted" };
116119
if (cls === "border-dashed") return { borderStyle: "dashed" };
117120

118121
// Border width (border-0, border-t, border-[8px], etc.)
119122
if (cls.startsWith("border-")) {
120-
return parseBorderWidth(cls);
123+
return parseBorderWidth(cls, customColors);
121124
}
122125

123126
if (cls === "border") {
@@ -134,14 +137,26 @@ export function parseBorder(cls: string): StyleObject | null {
134137

135138
/**
136139
* Parse border width classes
140+
* @param cls - The class name to parse
141+
* @param customColors - Optional custom colors (passed to parseColor for pattern detection)
137142
*/
138-
function parseBorderWidth(cls: string): StyleObject | null {
143+
function parseBorderWidth(cls: string, customColors?: Record<string, string>): StyleObject | null {
139144
// Directional borders: border-t, border-t-2, border-t-[8px]
145+
// Note: border-x and border-y are handled by parseColor for colors only
140146
const dirMatch = cls.match(/^border-([trbl])(?:-(.+))?$/);
141147
if (dirMatch) {
142148
const dir = dirMatch[1];
143149
const valueStr = dirMatch[2] || ""; // empty string for border-t
144150

151+
// If it's a color pattern, let parseColor handle it
152+
// Try to parse as color - if it succeeds, return null (let parseColor handle it)
153+
if (valueStr) {
154+
const colorResult = parseColor(cls, customColors);
155+
if (colorResult !== null) {
156+
return null; // It's a color, let parseColor handle it
157+
}
158+
}
159+
145160
// Try arbitrary value first (if it starts with [)
146161
if (valueStr.startsWith("[")) {
147162
const arbitraryValue = parseArbitraryBorderWidth(valueStr);

src/parser/colors.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,5 +178,43 @@ export function parseColor(cls: string, customColors?: Record<string, string>):
178178
}
179179
}
180180

181+
// Directional border colors: border-t-red-500, border-l-blue-500/50, border-r-[#ff0000]
182+
const dirBorderMatch = cls.match(/^border-([trblxy])-(.+)$/);
183+
if (dirBorderMatch) {
184+
const dir = dirBorderMatch[1];
185+
const colorKey = dirBorderMatch[2];
186+
187+
// Skip arbitrary values that don't look like colors (e.g., border-t-[3px] is width)
188+
if (colorKey.startsWith("[") && !colorKey.startsWith("[#")) {
189+
return null;
190+
}
191+
192+
const color = parseColorWithOpacity(colorKey);
193+
if (color) {
194+
// Map direction to React Native property/properties
195+
// x and y apply to multiple sides (RN doesn't have borderHorizontalColor/borderVerticalColor)
196+
if (dir === "x") {
197+
return {
198+
borderLeftColor: color,
199+
borderRightColor: color,
200+
};
201+
}
202+
if (dir === "y") {
203+
return {
204+
borderTopColor: color,
205+
borderBottomColor: color,
206+
};
207+
}
208+
209+
const propMap: Record<string, string> = {
210+
t: "borderTopColor",
211+
r: "borderRightColor",
212+
b: "borderBottomColor",
213+
l: "borderLeftColor",
214+
};
215+
return { [propMap[dir]]: color };
216+
}
217+
}
218+
181219
return null;
182220
}

src/parser/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ export function parseClassName(className: string, customTheme?: CustomTheme): St
5050
export function parseClass(cls: string, customTheme?: CustomTheme): StyleObject {
5151
// Try each parser in order
5252
// Note: parseBorder must come before parseColor to avoid border-[3px] being parsed as a color
53-
// parseColor and parseTypography get custom theme, others don't need it
53+
// parseBorder, parseColor and parseTypography get custom theme
5454
const parsers: Array<(cls: string) => StyleObject | null> = [
5555
parseSpacing,
56-
parseBorder,
56+
(cls: string) => parseBorder(cls, customTheme?.colors),
5757
(cls: string) => parseColor(cls, customTheme?.colors),
5858
parseLayout,
5959
(cls: string) => parseTypography(cls, customTheme?.fontFamily, customTheme?.fontSize),

0 commit comments

Comments
 (0)