diff --git a/packages/alfa-rules/src/sia-r62/rule.ts b/packages/alfa-rules/src/sia-r62/rule.ts index 7c4ee30e25..12bba61058 100644 --- a/packages/alfa-rules/src/sia-r62/rule.ts +++ b/packages/alfa-rules/src/sia-r62/rule.ts @@ -360,11 +360,14 @@ export namespace ComputedStyles { "outline-style" )} ${longhand("outline-style")}`.trim(); - // While text-decoration-style is not important for deciding if there is one, - // it is important for rendering the link with the correct styling. + // While text-decoration-style and text-decoration-thickness are not + // important for deciding if there is one, but they are important for + // rendering the link with the correct styling. const textDecoration = `${longhand("text-decoration-line")} ${longhand( "text-decoration-color" - )} ${longhand("text-decoration-style")}`.trim(); + )} ${longhand("text-decoration-style")} ${longhand( + "text-decoration-thickness" + )}`.trim(); const longhands = ([ "background-color", diff --git a/packages/alfa-style/src/index.ts b/packages/alfa-style/src/index.ts index 6071b98438..7e5acd668d 100644 --- a/packages/alfa-style/src/index.ts +++ b/packages/alfa-style/src/index.ts @@ -115,6 +115,7 @@ import "./property/text-decoration"; import "./property/text-decoration-color"; import "./property/text-decoration-line"; import "./property/text-decoration-style"; +import "./property/text-decoration-thickness"; import "./property/text-indent"; import "./property/text-overflow"; import "./property/text-transform"; diff --git a/packages/alfa-style/src/property/text-decoration-thickness.ts b/packages/alfa-style/src/property/text-decoration-thickness.ts new file mode 100644 index 0000000000..03e55f4642 --- /dev/null +++ b/packages/alfa-style/src/property/text-decoration-thickness.ts @@ -0,0 +1,64 @@ +import { Keyword, Length, Percentage, Token } from "@siteimprove/alfa-css"; +import { Parser } from "@siteimprove/alfa-parser"; +import { Slice } from "@siteimprove/alfa-slice"; + +import { Property } from "../property"; +import { Resolver } from "../resolver"; +import { Value } from "../value"; + +const { either } = Parser; + +declare module "../property" { + interface Longhands { + "text-decoration-thickness": Property; + } +} + +/** + * @internal + */ +export type Specified = + | Length + | Percentage + | Keyword<"auto"> + | Keyword<"from-font">; + +/** + * @internal + */ +export type Computed = Length<"px"> | Keyword<"auto"> | Keyword<"from-font">; + +/** + * @internal + */ +export const parse = either, Specified, string>( + Keyword.parse("auto", "from-font"), + Length.parse, + Percentage.parse +); + +/** + * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/outline-width} + * @internal + */ +export default Property.register( + "text-decoration-thickness", + Property.of( + Keyword.of("auto"), + parse, + (thickness, style) => + thickness.map((value) => { + switch (value.type) { + case "keyword": + return value; + case "length": + return Resolver.length(value, style); + case "percentage": + return Length.of( + style.computed("font-size").value.value * value.value, + "px" + ); + } + }) + ) +); diff --git a/packages/alfa-style/src/property/text-decoration.ts b/packages/alfa-style/src/property/text-decoration.ts index e9f4051ffc..878f492179 100644 --- a/packages/alfa-style/src/property/text-decoration.ts +++ b/packages/alfa-style/src/property/text-decoration.ts @@ -6,11 +6,15 @@ import { Property } from "../property"; import * as Color from "./text-decoration-color"; import * as Line from "./text-decoration-line"; import * as Style from "./text-decoration-style"; +import * as Thickness from "./text-decoration-thickness"; declare module "../property" { interface Shorthands { "text-decoration": Property.Shorthand< - "text-decoration-line" | "text-decoration-style" | "text-decoration-color" + | "text-decoration-line" + | "text-decoration-style" + | "text-decoration-color" + | "text-decoration-thickness" >; } } @@ -22,11 +26,17 @@ declare module "../property" { export default Property.registerShorthand( "text-decoration", Property.shorthand( - ["text-decoration-line", "text-decoration-style", "text-decoration-color"], + [ + "text-decoration-line", + "text-decoration-style", + "text-decoration-color", + "text-decoration-thickness", + ], (input) => { let line: Line.Specified | undefined; let style: Style.Specified | undefined; let color: Color.Specified | undefined; + let thickness: Thickness.Specified | undefined; while (true) { for (const [remainder] of Token.parseWhitespace(input)) { @@ -60,11 +70,25 @@ export default Property.registerShorthand( } } + if (thickness === undefined) { + const result = Thickness.parse(input); + + if (result.isOk()) { + [input, thickness] = result.get(); + continue; + } + } + break; } - if (line === undefined && style === undefined && color === undefined) { - return Err.of(`Expected one of line, style, or color`); + if ( + line === undefined && + style === undefined && + color === undefined && + thickness === undefined + ) { + return Err.of(`Expected one of line, style, color, or thickness`); } return Result.of([ @@ -73,6 +97,7 @@ export default Property.registerShorthand( ["text-decoration-line", line ?? Keyword.of("initial")], ["text-decoration-style", style ?? Keyword.of("initial")], ["text-decoration-color", color ?? Keyword.of("initial")], + ["text-decoration-thickness", thickness ?? Keyword.of("initial")], ], ]); } diff --git a/packages/alfa-style/test/property/text-decoration.spec.tsx b/packages/alfa-style/test/property/text-decoration.spec.tsx index dc7e0db954..589bc023e3 100644 --- a/packages/alfa-style/test/property/text-decoration.spec.tsx +++ b/packages/alfa-style/test/property/text-decoration.spec.tsx @@ -160,3 +160,54 @@ test("#cascaded() parses `text-decoration: underline solid red`", (t) => { source: h.declaration("text-decoration", "underline solid red").toJSON(), }); }); + +test("#cascaded() parses `text-decoration: underline solid red 2px`", (t) => { + const element =
; + + const declaration = h.declaration( + "text-decoration", + "underline solid red 2px" + ); + + const style = Style.from(element, device); + + t.deepEqual(style.cascaded("text-decoration-line").get().toJSON(), { + value: { + type: "list", + values: [ + { + type: "keyword", + value: "underline", + }, + ], + separator: " ", + }, + source: declaration.toJSON(), + }); + + t.deepEqual(style.cascaded("text-decoration-style").get().toJSON(), { + value: { + type: "keyword", + value: "solid", + }, + source: declaration.toJSON(), + }); + + t.deepEqual(style.cascaded("text-decoration-color").get().toJSON(), { + value: { + type: "color", + format: "named", + color: "red", + }, + source: declaration.toJSON(), + }); + + t.deepEqual(style.cascaded("text-decoration-thickness").get().toJSON(), { + value: { + type: "length", + value: 2, + unit: "px", + }, + source: declaration.toJSON(), + }); +}); diff --git a/packages/alfa-style/tsconfig.json b/packages/alfa-style/tsconfig.json index dda9f4dc68..4644a62e04 100644 --- a/packages/alfa-style/tsconfig.json +++ b/packages/alfa-style/tsconfig.json @@ -120,6 +120,7 @@ "src/property/text-decoration-color.ts", "src/property/text-decoration-line.ts", "src/property/text-decoration-style.ts", + "src/property/text-decoration-thickness.ts", "src/property/text-indent.ts", "src/property/text-overflow.ts", "src/property/text-transform.ts",