From 4115edc0759bd2d17d3195a409dedc4536f283d4 Mon Sep 17 00:00:00 2001 From: Joonas Tiala Date: Mon, 31 Jul 2023 15:49:30 +0300 Subject: [PATCH 1/4] feat: add Anchor component --- apps/docs/.storybook/preview.ts | 8 +- .../stories/Navigation/Anchor.stories.tsx | 29 +++++ apps/nextjs-example/app/layout.tsx | 24 ++-- apps/nextjs-example/app/page.tsx | 7 +- apps/nextjs-example/posts/lorem-ipsum.mdx | 2 +- apps/nextjs-example/utils/mdx.tsx | 7 +- package.json | 2 +- .../src/components/Anchor/Anchor.test.tsx | 55 +++++++++ .../react/src/components/Anchor/Anchor.tsx | 115 ++++++++++++++++++ .../Anchor/__snapshots__/Anchor.test.tsx.snap | 23 ++++ packages/react/src/components/Anchor/index.ts | 2 + packages/react/src/components/index.ts | 1 + packages/style/src/components/anchor.css | 22 ++++ packages/style/src/index.css | 1 + 14 files changed, 280 insertions(+), 18 deletions(-) create mode 100644 apps/docs/stories/Navigation/Anchor.stories.tsx create mode 100644 packages/react/src/components/Anchor/Anchor.test.tsx create mode 100644 packages/react/src/components/Anchor/Anchor.tsx create mode 100644 packages/react/src/components/Anchor/__snapshots__/Anchor.test.tsx.snap create mode 100644 packages/react/src/components/Anchor/index.ts create mode 100644 packages/style/src/components/anchor.css diff --git a/apps/docs/.storybook/preview.ts b/apps/docs/.storybook/preview.ts index 2488a3f..42b561a 100644 --- a/apps/docs/.storybook/preview.ts +++ b/apps/docs/.storybook/preview.ts @@ -5,7 +5,13 @@ const preview: Preview = { parameters: { options: { storySort: { - order: ["Introduction", "Development", "Typography", "Form"], + order: [ + "Introduction", + "Development", + "Typography", + "Navigation", + "Form", + ], }, }, actions: { argTypesRegex: "^on[A-Z].*" }, diff --git a/apps/docs/stories/Navigation/Anchor.stories.tsx b/apps/docs/stories/Navigation/Anchor.stories.tsx new file mode 100644 index 0000000..3e1353a --- /dev/null +++ b/apps/docs/stories/Navigation/Anchor.stories.tsx @@ -0,0 +1,29 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Anchor } from "@themeless-ui/react"; + +const meta = { + title: "Navigation/Anchor", + component: Anchor, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const href = "https://example.com"; +const children = "Example.com"; + +export const Default: Story = { + args: { + href, + children, + }, +}; + +export const ExternalLink: Story = { + args: { + href, + target: "_blank", + children, + }, +}; diff --git a/apps/nextjs-example/app/layout.tsx b/apps/nextjs-example/app/layout.tsx index fe60d87..60edd78 100644 --- a/apps/nextjs-example/app/layout.tsx +++ b/apps/nextjs-example/app/layout.tsx @@ -1,4 +1,4 @@ -import { Heading, Text } from "@themeless-ui/react"; +import { Anchor, Heading, Text } from "@themeless-ui/react"; import type { Metadata } from "next"; import Link from "next/link"; import "./index.css"; @@ -22,31 +22,33 @@ export default function RootLayout({ ThemelessUI Next.js Example App
{children}
- {/* TODO: use */} - GitHub + GitHub {" | "} - Storybook + Storybook
diff --git a/apps/nextjs-example/app/page.tsx b/apps/nextjs-example/app/page.tsx index 16e7984..7de9f5e 100644 --- a/apps/nextjs-example/app/page.tsx +++ b/apps/nextjs-example/app/page.tsx @@ -1,4 +1,4 @@ -import { Heading, List, Text } from "@themeless-ui/react"; +import { Anchor, Heading, List, Text } from "@themeless-ui/react"; import { allPosts } from "contentlayer/generated"; import { compareDesc } from "date-fns"; import Link from "next/link"; @@ -16,8 +16,9 @@ export default async function Home() { {posts.map((post) => ( - {/* TODO: use */} - {post.title} + + {post.title} + ))} diff --git a/apps/nextjs-example/posts/lorem-ipsum.mdx b/apps/nextjs-example/posts/lorem-ipsum.mdx index a6baaf7..9fbbefa 100644 --- a/apps/nextjs-example/posts/lorem-ipsum.mdx +++ b/apps/nextjs-example/posts/lorem-ipsum.mdx @@ -9,7 +9,7 @@ date: 2023-01-01 ### Ipsum in blandit -Integer hendrerit, ipsum in blandit rhoncus, orci sapien ullamcorper nibh, vitae fringilla lectus velit eget magna. Curabitur erat justo, dictum vitae bibendum at, lacinia non dolor. Vestibulum consequat hendrerit sem vel consequat. Duis pellentesque volutpat justo sed elementum. Curabitur euismod tempor mollis. +Integer hendrerit, [ipsum in blandit](https://exaple.com) rhoncus, orci sapien ullamcorper nibh, vitae fringilla lectus velit eget magna. Curabitur erat justo, dictum vitae bibendum at, lacinia non dolor. Vestibulum consequat hendrerit sem vel consequat. Duis pellentesque volutpat justo sed elementum. Curabitur euismod tempor mollis. - Nullam efficitur quis urna eget pellentesque. - Cras elementum ullamcorper odio, dapibus ultrices justo mattis ut. diff --git a/apps/nextjs-example/utils/mdx.tsx b/apps/nextjs-example/utils/mdx.tsx index e5c6c15..6a245a2 100644 --- a/apps/nextjs-example/utils/mdx.tsx +++ b/apps/nextjs-example/utils/mdx.tsx @@ -1,4 +1,5 @@ import { + Anchor, Blockquote, Heading, List, @@ -9,7 +10,11 @@ import type { MDXComponents } from "mdx/types"; export const mdxComponents: MDXComponents = { p: ({ children }) => {children}, - a: ({ children }) => {children}, // TODO: use + a: ({ href, target, children }) => ( + + {children} + + ), em: ({ children }) => {children}, strong: ({ children }) => {children}, blockquote: ({ children }) =>
{children}
, diff --git a/package.json b/package.json index 3845299..d140ed4 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ ], "packageManager": "pnpm@8.6.10", "scripts": { - "dev": "turbo run dev --no-cache --continue", + "dev": "turbo run dev --no-cache --continue --concurrency=12", "build": "turbo run build", "preview": "turbo run preview", "test": "turbo run test", diff --git a/packages/react/src/components/Anchor/Anchor.test.tsx b/packages/react/src/components/Anchor/Anchor.test.tsx new file mode 100644 index 0000000..a909580 --- /dev/null +++ b/packages/react/src/components/Anchor/Anchor.test.tsx @@ -0,0 +1,55 @@ +import { + componentToJson, + describe, + expect, + it, + render, + renderer, + screen, +} from "../../../test"; +import { Anchor } from "./Anchor"; + +const href = "https://example.com"; +const children = "Example.com"; + +const defaultAnchor = ( + + {children} + +); + +const externalAnchor = ( + + {children} + +); + +describe("Anchor", async () => { + it("should render default anchor", () => { + expect(componentToJson(renderer.create(defaultAnchor))).toMatchSnapshot(); + + render(defaultAnchor); + + const anchor = screen.getByTestId("anchor"); + const linkRelationships = anchor.getAttribute("rel"); + + expect(anchor).toBeInTheDocument(); + expect(anchor).toHaveTextContent(children); + expect(linkRelationships).toBeFalsy(); + }); + + it("should render external anchor", () => { + expect(componentToJson(renderer.create(externalAnchor))).toMatchSnapshot(); + + render(externalAnchor); + + const anchor = screen.getByTestId("anchor"); + const linkRelationships = (anchor.getAttribute("rel") || "").split(" "); + + expect(anchor).toBeInTheDocument(); + expect(anchor).toHaveTextContent(children); + expect(anchor).toHaveAttribute("target", "_blank"); + expect(linkRelationships).toContain("noopener"); + expect(linkRelationships).toContain("noreferrer"); + }); +}); diff --git a/packages/react/src/components/Anchor/Anchor.tsx b/packages/react/src/components/Anchor/Anchor.tsx new file mode 100644 index 0000000..00a2f34 --- /dev/null +++ b/packages/react/src/components/Anchor/Anchor.tsx @@ -0,0 +1,115 @@ +import { CommonComponentProps, cn } from "@themeless-ui/utils"; +import { AnchorHTMLAttributes, ReactNode } from "react"; + +type HTMLAnchorProps = AnchorHTMLAttributes; + +export type AnchorProps = { + /** + * The URL that the hyperlink points to. + */ + href?: HTMLAnchorProps["href"]; + + /** + * The relationship of the linked URL. + * + * If `rel` is not defined and the anchor has `target` as `_blank`, the following relationships will be added automatically: + * - `noopener` to prevent reverse tabnabbing + * - `noreferrer` to prevent passing referrer information to the target + */ + rel?: HTMLAnchorProps["rel"]; + + /** + * Where to display the linked URL. + */ + target?: AnchorHTMLAttributes["target"]; + + /** + * Causes the browser to treat the linked URL as a download. + */ + download?: AnchorHTMLAttributes["download"]; + + /** + * A function that is called when the anchor is clicked. + */ + onClick?: AnchorHTMLAttributes["onClick"]; + + /** + * A function that is called when the anchor is focused. + */ + onFocus?: AnchorHTMLAttributes["onFocus"]; + + /** + * A function that is called when the anchor hast lost focus. + */ + onBlur?: AnchorHTMLAttributes["onBlur"]; + + /** + * A function that is called when mouse enters the anchor. + */ + onMouseEnter?: AnchorHTMLAttributes["onMouseEnter"]; + + /** + * A function that is called when mouse leaves the anchor. + */ + onMouseLeave?: AnchorHTMLAttributes["onMouseLeave"]; + + /** + * A function that is called when one or more touch points are placed on the touch surface. + */ + onTouchStart?: AnchorHTMLAttributes["onTouchStart"]; + + /** + * A function that is called when one or more touch points are removed from the touch surface. + */ + onTouchEnd?: AnchorHTMLAttributes["onTouchEnd"]; + + /** + * Anchor content. + */ + children?: ReactNode; +} & CommonComponentProps; + +const className = cn("anchor"); + +/** + * Display a hyperlink. + */ +export function Anchor({ + href, + rel, + target, + download, + onClick, + onFocus, + onBlur, + onMouseEnter, + onMouseLeave, + onTouchStart, + onTouchEnd, + children, + testId, +}: AnchorProps) { + const openInNewTab = target === "_blank"; + const linkRelationships = + rel || openInNewTab ? "noopener noreferrer" : undefined; + + return ( + + {children} + + ); +} diff --git a/packages/react/src/components/Anchor/__snapshots__/Anchor.test.tsx.snap b/packages/react/src/components/Anchor/__snapshots__/Anchor.test.tsx.snap new file mode 100644 index 0000000..b80946e --- /dev/null +++ b/packages/react/src/components/Anchor/__snapshots__/Anchor.test.tsx.snap @@ -0,0 +1,23 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Anchor > should render default anchor 1`] = ` + + Example.com + +`; + +exports[`Anchor > should render external anchor 1`] = ` + + Example.com + +`; diff --git a/packages/react/src/components/Anchor/index.ts b/packages/react/src/components/Anchor/index.ts new file mode 100644 index 0000000..5464ef7 --- /dev/null +++ b/packages/react/src/components/Anchor/index.ts @@ -0,0 +1,2 @@ +export { Anchor } from "./Anchor"; +export type { AnchorProps } from "./Anchor"; diff --git a/packages/react/src/components/index.ts b/packages/react/src/components/index.ts index 4ef1a3a..31d5412 100644 --- a/packages/react/src/components/index.ts +++ b/packages/react/src/components/index.ts @@ -1,3 +1,4 @@ +export * from "./Anchor"; export * from "./Blockquote"; export * from "./Button"; export * from "./Heading"; diff --git a/packages/style/src/components/anchor.css b/packages/style/src/components/anchor.css new file mode 100644 index 0000000..708df78 --- /dev/null +++ b/packages/style/src/components/anchor.css @@ -0,0 +1,22 @@ +.tui__anchor, +.tui__anchor:link, +.tui__anchor:visited { + display: inline; + box-sizing: border-box; + color: blue; + cursor: auto; + font-family: var(--tui__font-family-serif); + font-size: var(--tui__font-size-md); + -webkit-font-smoothing: var(--tui__font-smoothing-webkit); + -moz-osx-font-smoothing: var(--tui__font-smoothing-moz); + font-weight: var(--tui__font-weight-normal); + line-height: var(--tui__line-height-md); + text-decoration: underline; +} + +a:link:hover, +a:link:active, +a:visited:active, +a:visited:hover { + color: darkblue; +} diff --git a/packages/style/src/index.css b/packages/style/src/index.css index 0cd2a8c..815e501 100644 --- a/packages/style/src/index.css +++ b/packages/style/src/index.css @@ -7,6 +7,7 @@ @import "./tokens/colors.css"; /* Components */ +@import "./components/anchor.css"; @import "./components/blockquote.css"; @import "./components/button.css"; @import "./components/heading.css"; From 699585a6c43726f8a8067e574f01083ffb43b5e0 Mon Sep 17 00:00:00 2001 From: Joonas Tiala Date: Mon, 31 Jul 2023 15:51:01 +0300 Subject: [PATCH 2/4] test: better naming in tests --- .../components/Blockquote/Blockquote.test.tsx | 20 ++++++++++--------- .../src/components/Heading/Heading.test.tsx | 6 +++--- .../react/src/components/List/List.test.tsx | 14 ++++++------- .../components/Paragraph/Paragraph.test.tsx | 6 +++--- .../react/src/components/Text/Text.test.tsx | 6 +++--- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/packages/react/src/components/Blockquote/Blockquote.test.tsx b/packages/react/src/components/Blockquote/Blockquote.test.tsx index 6466950..8a9dee4 100644 --- a/packages/react/src/components/Blockquote/Blockquote.test.tsx +++ b/packages/react/src/components/Blockquote/Blockquote.test.tsx @@ -17,19 +17,19 @@ const lipsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec accumsan ex vel lacinia volutpat. Ut ultricies, mauris et varius finibus, nulla ante efficitur libero, eget convallis odio ex ut neque. Praesent quis odio ac felis pretium pharetra sit amet quis neque. Nam nec libero purus. Maecenas sagittis lobortis lacinia. Aliquam non vehicula metus. Vivamus fringilla ante eget leo pretium cursus. Morbi venenatis diam metus, ut faucibus dolor mollis a. Nulla nec ex sapien. Curabitur a tempor est. Mauris faucibus congue mi, sit amet pulvinar justo porttitor nec. Nunc et eros a est fermentum lacinia id at nisi."; const children = {lipsum}; -const elementDefault = ( +const defaultBlockquote = (
{children}
); -const elementWithoutSource = ( +const blockquoteWithoutSource = (
{children}
); -const elementWithoutAuthor = ( +const blockquoteWithoutAuthor = (
{children}
@@ -37,9 +37,11 @@ const elementWithoutAuthor = ( describe("Blockquote", async () => { it("should render default blockquote", () => { - expect(componentToJson(renderer.create(elementDefault))).toMatchSnapshot(); + expect( + componentToJson(renderer.create(defaultBlockquote)), + ).toMatchSnapshot(); - render(elementDefault); + render(defaultBlockquote); const blockquote = screen.getByTestId("blockquote"); const footer = blockquote.querySelector("footer"); @@ -52,10 +54,10 @@ describe("Blockquote", async () => { it("should render author without source", () => { expect( - componentToJson(renderer.create(elementWithoutSource)), + componentToJson(renderer.create(blockquoteWithoutSource)), ).toMatchSnapshot(); - render(elementWithoutSource); + render(blockquoteWithoutSource); const blockquote = screen.getByTestId("blockquote"); const footer = blockquote.querySelector("footer"); @@ -65,10 +67,10 @@ describe("Blockquote", async () => { it("should render source without author", () => { expect( - componentToJson(renderer.create(elementWithoutAuthor)), + componentToJson(renderer.create(blockquoteWithoutAuthor)), ).toMatchSnapshot(); - render(elementWithoutAuthor); + render(blockquoteWithoutAuthor); const blockquote = screen.getByTestId("blockquote"); const footer = blockquote.querySelector("footer"); diff --git a/packages/react/src/components/Heading/Heading.test.tsx b/packages/react/src/components/Heading/Heading.test.tsx index 7eefe8e..938f849 100644 --- a/packages/react/src/components/Heading/Heading.test.tsx +++ b/packages/react/src/components/Heading/Heading.test.tsx @@ -10,7 +10,7 @@ import { } from "../../../test"; import { Heading } from "./Heading"; -const element = ( +const defaultHeading = ( Lorem ipsum dolor sit amet. @@ -18,9 +18,9 @@ const element = ( describe("Heading", async () => { it("should render the heading", () => { - expect(componentToJson(renderer.create(element))).toMatchSnapshot(); + expect(componentToJson(renderer.create(defaultHeading))).toMatchSnapshot(); - render(element); + render(defaultHeading); const heading = screen.getByTestId("heading"); diff --git a/packages/react/src/components/List/List.test.tsx b/packages/react/src/components/List/List.test.tsx index a79ac14..514fec5 100644 --- a/packages/react/src/components/List/List.test.tsx +++ b/packages/react/src/components/List/List.test.tsx @@ -15,8 +15,8 @@ const items = [ Consectetur adipiscing elit, ]; -const elementUnordered = {items}; -const elementOrdered = ( +const unorderedList = {items}; +const orderedList = ( {items} @@ -24,11 +24,9 @@ const elementOrdered = ( describe("List", async () => { it("should render unordered list", () => { - expect( - componentToJson(renderer.create(elementUnordered)), - ).toMatchSnapshot(); + expect(componentToJson(renderer.create(unorderedList))).toMatchSnapshot(); - render(elementUnordered); + render(unorderedList); const list = screen.getByTestId("list"); const listItems = list.querySelectorAll("li"); @@ -42,9 +40,9 @@ describe("List", async () => { }); it("should render ordered list", () => { - expect(componentToJson(renderer.create(elementOrdered))).toMatchSnapshot(); + expect(componentToJson(renderer.create(orderedList))).toMatchSnapshot(); - render(elementOrdered); + render(orderedList); const list = screen.getByTestId("list"); diff --git a/packages/react/src/components/Paragraph/Paragraph.test.tsx b/packages/react/src/components/Paragraph/Paragraph.test.tsx index 1a1d193..a37aecd 100644 --- a/packages/react/src/components/Paragraph/Paragraph.test.tsx +++ b/packages/react/src/components/Paragraph/Paragraph.test.tsx @@ -9,15 +9,15 @@ import { } from "../../../test"; import { Paragraph } from "./Paragraph"; -const element = ( +const defaultParagaph = ( Lorem ipsum dolor sit amet. ); describe("Paragraph", async () => { it("should render the paragraph", () => { - expect(componentToJson(renderer.create(element))).toMatchSnapshot(); + expect(componentToJson(renderer.create(defaultParagaph))).toMatchSnapshot(); - render(element); + render(defaultParagaph); const paragraph = screen.getByTestId("paragraph"); diff --git a/packages/react/src/components/Text/Text.test.tsx b/packages/react/src/components/Text/Text.test.tsx index 1a2e3b1..cf56ee2 100644 --- a/packages/react/src/components/Text/Text.test.tsx +++ b/packages/react/src/components/Text/Text.test.tsx @@ -10,13 +10,13 @@ import { } from "../../../test"; import { Text, TextProps } from "./Text"; -const element = Lorem ipsum dolor sit amet.; +const defaultText = Lorem ipsum dolor sit amet.; describe("Text", async () => { it("should render the text", () => { - expect(componentToJson(renderer.create(element))).toMatchSnapshot(); + expect(componentToJson(renderer.create(defaultText))).toMatchSnapshot(); - render(element); + render(defaultText); const text = screen.getByTestId("text"); From e912ebdcdbce1d235dd7399762667b4fc9bd2309 Mon Sep 17 00:00:00 2001 From: Joonas Tiala Date: Mon, 31 Jul 2023 15:51:33 +0300 Subject: [PATCH 3/4] docs: simpler example --- apps/docs/stories/Typography/Text.stories.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/docs/stories/Typography/Text.stories.tsx b/apps/docs/stories/Typography/Text.stories.tsx index 8061e11..5b7e273 100644 --- a/apps/docs/stories/Typography/Text.stories.tsx +++ b/apps/docs/stories/Typography/Text.stories.tsx @@ -155,12 +155,12 @@ export const CombiningTextTypes = {
  • del and ins in cite - First verse of{" "} + Lorem ipsum{" "} - Think - Respect + dolor + sit amet - , by Aretha Franklin. + , consectetur adipiscing elit.
  • From dd9c95edafd49ee6e75243720ee67cd0dc17c535 Mon Sep 17 00:00:00 2001 From: Joonas Tiala Date: Mon, 31 Jul 2023 15:53:12 +0300 Subject: [PATCH 4/4] fix: add some missing css font rules --- packages/style/src/components/blockquote.css | 3 ++- packages/style/src/components/list.css | 3 ++- packages/style/src/components/paragraph.css | 3 ++- packages/style/src/components/text.css | 7 +++++++ 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/style/src/components/blockquote.css b/packages/style/src/components/blockquote.css index b081f18..44caf42 100644 --- a/packages/style/src/components/blockquote.css +++ b/packages/style/src/components/blockquote.css @@ -4,9 +4,10 @@ margin: var(--tui__spacing-16) var(--tui__spacing-32); color: var(--tui__color-black); font-family: var(--tui__font-family-serif); - font-size: var(--tui__font-size-medium); + font-size: var(--tui__font-size-md); -webkit-font-smoothing: var(--tui__font-smoothing-webkit); -moz-osx-font-smoothing: var(--tui__font-smoothing-moz); + font-weight: var(--tui__font-weight-normal); line-height: var(--tui__line-height-md); overflow-wrap: break-word; } diff --git a/packages/style/src/components/list.css b/packages/style/src/components/list.css index 94fd6db..ef3e6fa 100644 --- a/packages/style/src/components/list.css +++ b/packages/style/src/components/list.css @@ -17,9 +17,10 @@ ol.tui__list { box-sizing: border-box; color: var(--tui__color-black); font-family: var(--tui__font-family-serif); - font-size: var(--tui__font-size-medium); + font-size: var(--tui__font-size-md); -webkit-font-smoothing: var(--tui__font-smoothing-webkit); -moz-osx-font-smoothing: var(--tui__font-smoothing-moz); + font-weight: var(--tui__font-weight-normal); line-height: var(--tui__line-height-md); overflow-wrap: break-word; } diff --git a/packages/style/src/components/paragraph.css b/packages/style/src/components/paragraph.css index 49d0018..5280905 100644 --- a/packages/style/src/components/paragraph.css +++ b/packages/style/src/components/paragraph.css @@ -4,9 +4,10 @@ margin: var(--tui__spacing-16) 0; color: var(--tui__color-black); font-family: var(--tui__font-family-serif); - font-size: var(--tui__font-size-medium); + font-size: var(--tui__font-size-md); -webkit-font-smoothing: var(--tui__font-smoothing-webkit); -moz-osx-font-smoothing: var(--tui__font-smoothing-moz); + font-weight: var(--tui__font-weight-normal); line-height: var(--tui__line-height-md); overflow-wrap: break-word; } diff --git a/packages/style/src/components/text.css b/packages/style/src/components/text.css index 85aaf28..96ddbeb 100644 --- a/packages/style/src/components/text.css +++ b/packages/style/src/components/text.css @@ -1,8 +1,10 @@ .tui__text { display: inline; box-sizing: border-box; + font-size: var(--tui__font-size-md); -webkit-font-smoothing: var(--tui__font-smoothing-webkit); -moz-osx-font-smoothing: var(--tui__font-smoothing-moz); + line-height: var(--tui__line-height-md); } abbr[title].tui__text { @@ -70,6 +72,11 @@ small.tui__text { font-size: var(--tui__font-size-sm); } +span.tui__text { + font-family: var(--tui__font-family-serif); + font-weight: var(--tui__font-weight-normal); +} + strong.tui__text { font-weight: var(--tui__font-weight-bold); }