Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,12 @@ export default withReactNativeCSS(defaultConfig, {
});
```

## Caveats

### `text-align` on wrapper elements

On native, `textAlign` is resolved from the `Text` component's own text attributes and is **not** inherited across a `View` → `Text` boundary. Applying `text-start` or `text-end` (or any `text-align` utility) to a wrapper `View` will compile correctly but have no visual effect. Apply the class to the `Text` itself, or use flex alignment (e.g. `items-start` / `items-end`) on the wrapper.

## Contributing

See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
Expand Down
31 changes: 31 additions & 0 deletions src/__tests__/vendor/tailwind/typography.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,37 @@ describe("Typography - Text Align", () => {
props: { style: { textAlign: "justify" } },
});
});
test("text-start", async () => {
expect(await renderCurrentTest()).toStrictEqual({
props: { style: { textAlign: "left" } },
});
});
test("text-end", async () => {
expect(await renderCurrentTest()).toStrictEqual({
props: { style: { textAlign: "right" } },
});
});
test("text-start is RTL-safe and compiles identically to text-left", async () => {
await expect(
renderSimple({ className: "text-start" }),
).resolves.toStrictEqual(await renderSimple({ className: "text-left" }));
});
test("text-end is RTL-safe and compiles identically to text-right", async () => {
await expect(
renderSimple({ className: "text-end" }),
).resolves.toStrictEqual(await renderSimple({ className: "text-right" }));
});
test("unsupported text-align value drops with a warning (no over-match)", async () => {
expect(
await renderSimple({
className: "text-match-parent",
extraCss: ".text-match-parent { text-align: match-parent; }",
}),
).toStrictEqual({
props: {},
warnings: { values: { "text-align": "match-parent" } },
});
});
});

describe("Typography - Text Color", () => {
Expand Down
29 changes: 27 additions & 2 deletions src/compiler/declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2398,15 +2398,40 @@ function parseGapValue(
}
}

const TEXT_ALIGN_ALLOWED = new Set([
Comment thread
mubeess marked this conversation as resolved.
Outdated
"auto",
"left",
"right",
"center",
"justify",
]);

export function parseTextAlign(
{ value }: DeclarationType<"text-align">,
builder: StylesheetBuilder,
) {
const allowed = new Set(["auto", "left", "right", "center", "justify"]);
if (allowed.has(value)) {
if (TEXT_ALIGN_ALLOWED.has(value)) {
return value;
}

// React Native's text alignment enum has no start/end values. Its native
// text layout resolves left/right logically for RTL, so these aliases are
// exact on both platforms. Do not branch on I18nManager here: that would
// invert the logical meaning under RTL.
//
// lightningcss normalises the parsed text-align enum to lowercase, so the
// strict === comparisons below are sufficient — no toLowerCase() needed.
// This differs from the unparsed-token path used by the color: inherit fix
// (#391), which preserves author casing and does need toLowerCase(). The
// asymmetry is correct; do not "fix" it.
if (value === "start") {
return "left";
}

if (value === "end") {
return "right";
}

builder.addWarning("value", value);
return undefined;
}
Expand Down