diff --git a/packages/tach-typography/README.md b/packages/tach-typography/README.md
index f46b462..cd5b4ab 100644
--- a/packages/tach-typography/README.md
+++ b/packages/tach-typography/README.md
@@ -58,6 +58,14 @@ export const Example = () => (
);
```
+Markdown as a prop (keeps existing API):
+
+```tsx
+
-
-
-
-
-
-
${escapeHtml(code)}`);
+ return token;
+ })
+ .replace(/\[([^\]\n]+)\]\(([^)\n]+)\)/g, (_match, label, hrefRaw) => {
+ const token = `${TOKEN_PREFIX}${tokenId++}`;
+ const href = sanitizeHref(hrefRaw);
+ if (!href) {
+ tokenMap.set(token, escapeHtml(label));
+ return token;
+ }
+ tokenMap.set(token, `${escapeHtml(label)}`);
+ return token;
+ });
+ let html = escapeHtml(tokenized)
+ .replace(/\*\*([^*\n]+)\*\*/g, "$1")
+ .replace(/__([^_\n]+)__/g, "$1")
+ .replace(/\*([^*\n]+)\*/g, "$1")
+ .replace(/_([^_\n]+)_/g, "$1")
+ .replace(/~~([^~\n]+)~~/g, "${escapeHtml(code)}`
+ );
+ return token;
+ }).replace(/\[([^\]\n]+)\]\(([^)\n]+)\)/g, (_match, label, hrefRaw) => {
+ const token = `${TOKEN_PREFIX}${tokenId++}`;
+ const href = sanitizeHref(hrefRaw);
+ if (!href) {
+ tokenMap.set(token, escapeHtml(label));
+ return token;
+ }
+ tokenMap.set(
+ token,
+ `${escapeHtml(label)}`
+ );
+ return token;
+ });
+ let html = escapeHtml(tokenized).replace(/\*\*([^*\n]+)\*\*/g, "$1").replace(/__([^_\n]+)__/g, "$1").replace(/\*([^*\n]+)\*/g, "$1").replace(/_([^_\n]+)_/g, "$1").replace(/~~([^~\n]+)~~/g, "${escapeHtml(code)}`,\n );\n return token;\n })\n .replace(/\\[([^\\]\\n]+)\\]\\(([^)\\n]+)\\)/g, (_match, label: string, hrefRaw: string) => {\n const token = `${TOKEN_PREFIX}${tokenId++}`;\n const href = sanitizeHref(hrefRaw);\n\n if (!href) {\n tokenMap.set(token, escapeHtml(label));\n return token;\n }\n\n tokenMap.set(\n token,\n `${escapeHtml(label)}`,\n );\n return token;\n });\n\n let html = escapeHtml(tokenized)\n .replace(/\\*\\*([^*\\n]+)\\*\\*/g, \"$1\")\n .replace(/__([^_\\n]+)__/g, \"$1\")\n .replace(/\\*([^*\\n]+)\\*/g, \"$1\")\n .replace(/_([^_\\n]+)_/g, \"$1\")\n .replace(/~~([^~\\n]+)~~/g, \"${escapeHtml(code)}`
+ );
+ return token;
+ }).replace(/\[([^\]\n]+)\]\(([^)\n]+)\)/g, (_match, label, hrefRaw) => {
+ const token = `${TOKEN_PREFIX}${tokenId++}`;
+ const href = sanitizeHref(hrefRaw);
+ if (!href) {
+ tokenMap.set(token, escapeHtml(label));
+ return token;
+ }
+ tokenMap.set(
+ token,
+ `${escapeHtml(label)}`
+ );
+ return token;
+ });
+ let html = escapeHtml(tokenized).replace(/\*\*([^*\n]+)\*\*/g, "$1").replace(/__([^_\n]+)__/g, "$1").replace(/\*([^*\n]+)\*/g, "$1").replace(/_([^_\n]+)_/g, "$1").replace(/~~([^~\n]+)~~/g, "${escapeHtml(code)}`,\n );\n return token;\n })\n .replace(/\\[([^\\]\\n]+)\\]\\(([^)\\n]+)\\)/g, (_match, label: string, hrefRaw: string) => {\n const token = `${TOKEN_PREFIX}${tokenId++}`;\n const href = sanitizeHref(hrefRaw);\n\n if (!href) {\n tokenMap.set(token, escapeHtml(label));\n return token;\n }\n\n tokenMap.set(\n token,\n `${escapeHtml(label)}`,\n );\n return token;\n });\n\n let html = escapeHtml(tokenized)\n .replace(/\\*\\*([^*\\n]+)\\*\\*/g, \"$1\")\n .replace(/__([^_\\n]+)__/g, \"$1\")\n .replace(/\\*([^*\\n]+)\\*/g, \"$1\")\n .replace(/_([^_\\n]+)_/g, \"$1\")\n .replace(/~~([^~\\n]+)~~/g, \"${escapeHtml(code)}`
+ );
+ return token;
+ }).replace(/\[([^\]\n]+)\]\(([^)\n]+)\)/g, (_match, label, hrefRaw) => {
+ const token = `${TOKEN_PREFIX}${tokenId++}`;
+ const href = sanitizeHref(hrefRaw);
+ if (!href) {
+ tokenMap.set(token, escapeHtml(label));
+ return token;
+ }
+ tokenMap.set(
+ token,
+ `${escapeHtml(label)}`
+ );
+ return token;
+ });
+ let html = escapeHtml(tokenized).replace(/\*\*([^*\n]+)\*\*/g, "$1").replace(/__([^_\n]+)__/g, "$1").replace(/\*([^*\n]+)\*/g, "$1").replace(/_([^_\n]+)_/g, "$1").replace(/~~([^~\n]+)~~/g, "(\n Component: React.ComponentType
,\n variant: TypographyVariant,\n) => {\n const Variant = React.forwardRef (Component: React.ComponentType ) => ({\n LargeTitle: createTypographyVariant(Component, \"LargeTitle\"),\n Title1: createTypographyVariant(Component, \"Title1\"),\n Title2: createTypographyVariant(Component, \"Title2\"),\n Title3: createTypographyVariant(Component, \"Title3\"),\n Headline: createTypographyVariant(Component, \"Headline\"),\n Body: createTypographyVariant(Component, \"Body\"),\n Inputs: createTypographyVariant(Component, \"Inputs\"),\n Subheadline: createTypographyVariant(Component, \"Subheadline\"),\n FootnoteUnderline: createTypographyVariant(Component, \"FootnoteUnderline\"),\n Footnote: createTypographyVariant(Component, \"Footnote\"),\n Caption: createTypographyVariant(Component, \"Caption\"),\n Caption2: createTypographyVariant(Component, \"Caption2\"),\n\n AccentH1: createTypographyVariant(Component, \"AccentH1\"),\n AccentH2: createTypographyVariant(Component, \"AccentH2\"),\n AccentSubttl: createTypographyVariant(Component, \"AccentSubttl\"),\n AccentSubttl2: createTypographyVariant(Component, \"AccentSubttl2\"),\n AccentCaption: createTypographyVariant(Component, \"AccentCaption\"),\n AccentCaption2: createTypographyVariant(Component, \"AccentCaption2\"),\n AccentRegularM: createTypographyVariant(Component, \"AccentRegularM\"),\n AccentRegularS: createTypographyVariant(Component, \"AccentRegularS\"),\n AccentLargeTtl: createTypographyVariant(Component, \"AccentLargeTtl\"),\n AppMediumBody: createTypographyVariant(Component, \"AppMediumBody\"),\n AppMediumSubtext: createTypographyVariant(Component, \"AppMediumSubtext\"),\n AppMediumSubtextUnderline: createTypographyVariant(Component, \"AppMediumSubtextUnderline\"),\n});\n\nexport const TachTypography = {\n Text: createTypographyComponent (\n Component: React.ComponentType ,\n variant: TypographyVariant,\n) => {\n const Variant = React.forwardRef (Component: React.ComponentType ) => ({\n LargeTitle: createTypographyVariant(Component, \"LargeTitle\"),\n Title1: createTypographyVariant(Component, \"Title1\"),\n Title2: createTypographyVariant(Component, \"Title2\"),\n Title3: createTypographyVariant(Component, \"Title3\"),\n Headline: createTypographyVariant(Component, \"Headline\"),\n Body: createTypographyVariant(Component, \"Body\"),\n Inputs: createTypographyVariant(Component, \"Inputs\"),\n Subheadline: createTypographyVariant(Component, \"Subheadline\"),\n FootnoteUnderline: createTypographyVariant(Component, \"FootnoteUnderline\"),\n Footnote: createTypographyVariant(Component, \"Footnote\"),\n Caption: createTypographyVariant(Component, \"Caption\"),\n Caption2: createTypographyVariant(Component, \"Caption2\"),\n\n AccentH1: createTypographyVariant(Component, \"AccentH1\"),\n AccentH2: createTypographyVariant(Component, \"AccentH2\"),\n AccentSubttl: createTypographyVariant(Component, \"AccentSubttl\"),\n AccentSubttl2: createTypographyVariant(Component, \"AccentSubttl2\"),\n AccentCaption: createTypographyVariant(Component, \"AccentCaption\"),\n AccentCaption2: createTypographyVariant(Component, \"AccentCaption2\"),\n AccentRegularM: createTypographyVariant(Component, \"AccentRegularM\"),\n AccentRegularS: createTypographyVariant(Component, \"AccentRegularS\"),\n AccentLargeTtl: createTypographyVariant(Component, \"AccentLargeTtl\"),\n AppMediumBody: createTypographyVariant(Component, \"AppMediumBody\"),\n AppMediumSubtext: createTypographyVariant(Component, \"AppMediumSubtext\"),\n AppMediumSubtextUnderline: createTypographyVariant(Component, \"AppMediumSubtextUnderline\"),\n});\n\nexport const TachTypography = {\n Text: createTypographyComponent (\n Component: React.ComponentType ,\n variant: TypographyVariant,\n) => {\n const Variant = React.forwardRef (Component: React.ComponentType ) => ({\n LargeTitle: createTypographyVariant(Component, \"LargeTitle\"),\n Title1: createTypographyVariant(Component, \"Title1\"),\n Title2: createTypographyVariant(Component, \"Title2\"),\n Title3: createTypographyVariant(Component, \"Title3\"),\n Headline: createTypographyVariant(Component, \"Headline\"),\n Body: createTypographyVariant(Component, \"Body\"),\n Inputs: createTypographyVariant(Component, \"Inputs\"),\n Subheadline: createTypographyVariant(Component, \"Subheadline\"),\n FootnoteUnderline: createTypographyVariant(Component, \"FootnoteUnderline\"),\n Footnote: createTypographyVariant(Component, \"Footnote\"),\n Caption: createTypographyVariant(Component, \"Caption\"),\n Caption2: createTypographyVariant(Component, \"Caption2\"),\n\n AccentH1: createTypographyVariant(Component, \"AccentH1\"),\n AccentH2: createTypographyVariant(Component, \"AccentH2\"),\n AccentSubttl: createTypographyVariant(Component, \"AccentSubttl\"),\n AccentSubttl2: createTypographyVariant(Component, \"AccentSubttl2\"),\n AccentCaption: createTypographyVariant(Component, \"AccentCaption\"),\n AccentCaption2: createTypographyVariant(Component, \"AccentCaption2\"),\n AccentRegularM: createTypographyVariant(Component, \"AccentRegularM\"),\n AccentRegularS: createTypographyVariant(Component, \"AccentRegularS\"),\n AccentLargeTtl: createTypographyVariant(Component, \"AccentLargeTtl\"),\n AppMediumBody: createTypographyVariant(Component, \"AppMediumBody\"),\n AppMediumSubtext: createTypographyVariant(Component, \"AppMediumSubtext\"),\n AppMediumSubtextUnderline: createTypographyVariant(Component, \"AppMediumSubtextUnderline\"),\n});\n\nexport const TachTypography = {\n Text: createTypographyComponent (\n Component: React.ComponentType ,\n variant: TypographyVariant,\n) => {\n const Variant = React.forwardRef (Component: React.ComponentType ) => ({\n LargeTitle: createTypographyVariant(Component, \"LargeTitle\"),\n Title1: createTypographyVariant(Component, \"Title1\"),\n Title2: createTypographyVariant(Component, \"Title2\"),\n Title3: createTypographyVariant(Component, \"Title3\"),\n Headline: createTypographyVariant(Component, \"Headline\"),\n Body: createTypographyVariant(Component, \"Body\"),\n Inputs: createTypographyVariant(Component, \"Inputs\"),\n Subheadline: createTypographyVariant(Component, \"Subheadline\"),\n FootnoteUnderline: createTypographyVariant(Component, \"FootnoteUnderline\"),\n Footnote: createTypographyVariant(Component, \"Footnote\"),\n Caption: createTypographyVariant(Component, \"Caption\"),\n Caption2: createTypographyVariant(Component, \"Caption2\"),\n\n AccentH1: createTypographyVariant(Component, \"AccentH1\"),\n AccentH2: createTypographyVariant(Component, \"AccentH2\"),\n AccentSubttl: createTypographyVariant(Component, \"AccentSubttl\"),\n AccentSubttl2: createTypographyVariant(Component, \"AccentSubttl2\"),\n AccentCaption: createTypographyVariant(Component, \"AccentCaption\"),\n AccentCaption2: createTypographyVariant(Component, \"AccentCaption2\"),\n AccentRegularM: createTypographyVariant(Component, \"AccentRegularM\"),\n AccentRegularS: createTypographyVariant(Component, \"AccentRegularS\"),\n AccentLargeTtl: createTypographyVariant(Component, \"AccentLargeTtl\"),\n AppMediumBody: createTypographyVariant(Component, \"AppMediumBody\"),\n AppMediumSubtext: createTypographyVariant(Component, \"AppMediumSubtext\"),\n AppMediumSubtextUnderline: createTypographyVariant(Component, \"AppMediumSubtextUnderline\"),\n});\n\nexport const TachTypography = {\n Text: createTypographyComponent (
@@ -25,20 +28,45 @@ const createTypographyVariant = (
variant: TypographyVariant,
) => {
const Variant = React.forwardRef${escapeHtml(code)}`,\n );\n return token;\n })\n .replace(/\\[([^\\]\\n]+)\\]\\(([^)\\n]+)\\)/g, (_match, label: string, hrefRaw: string) => {\n const token = `${TOKEN_PREFIX}${tokenId++}`;\n const href = sanitizeHref(hrefRaw);\n\n if (!href) {\n tokenMap.set(token, escapeHtml(label));\n return token;\n }\n\n tokenMap.set(\n token,\n `${escapeHtml(label)}`,\n );\n return token;\n });\n\n let html = escapeHtml(tokenized)\n .replace(/\\*\\*([^*\\n]+)\\*\\*/g, \"$1\")\n .replace(/__([^_\\n]+)__/g, \"$1\")\n .replace(/\\*([^*\\n]+)\\*/g, \"$1\")\n .replace(/_([^_\\n]+)_/g, \"$1\")\n .replace(/~~([^~\\n]+)~~/g, \"$1\")\n .replace(/\\n/g, \"
\");\n\n html = html.replace(new RegExp(`${TOKEN_PREFIX}\\\\d+`, \"g\"), token => tokenMap.get(token) || token);\n\n return html;\n};\n","import React from \"react\";\n\nimport { Typography } from \"antd\";\nimport type { LinkProps } from \"antd/lib/typography/Link\";\nimport type { ParagraphProps } from \"antd/lib/typography/Paragraph\";\nimport type { TextProps } from \"antd/lib/typography/Text\";\nimport type { TitleProps } from \"antd/lib/typography/Title\";\n\nimport {\n tachTypographyMarkdownToHtml,\n tachTypographyClassName,\n type TypographyColor,\n type TypographyVariant,\n type TypographyWeight,\n} from \"../core\";\n\ninterface AdditionalProps {\n color?: TypographyColor;\n weight?: TypographyWeight;\n onClick?: (event: React.MouseEvent${escapeHtml(code)}`
+ );
+ return token;
+ }).replace(/\[([^\]\n]+)\]\(([^)\n]+)\)/g, (_match, label, hrefRaw) => {
+ const token = `${TOKEN_PREFIX}${tokenId++}`;
+ const href = sanitizeHref(hrefRaw);
+ if (!href) {
+ tokenMap.set(token, escapeHtml(label));
+ return token;
+ }
+ tokenMap.set(
+ token,
+ `${escapeHtml(label)}`
+ );
+ return token;
+ });
+ let html = escapeHtml(tokenized).replace(/\*\*([^*\n]+)\*\*/g, "$1").replace(/__([^_\n]+)__/g, "$1").replace(/\*([^*\n]+)\*/g, "$1").replace(/_([^_\n]+)_/g, "$1").replace(/~~([^~\n]+)~~/g, "$1").replace(/\n/g, "
");
+ html = html.replace(new RegExp(`${TOKEN_PREFIX}\\d+`, "g"), (token) => tokenMap.get(token) || token);
+ return html;
+};
var createTypographyVariant = (Component, variant) => {
const Variant = React.forwardRef(
- ({ color = "primary", weight = "normal", className, onClick, ...rest }, ref) => /* @__PURE__ */ jsx(
- Component,
- {
- ref,
- className: tachTypographyClassName({
- variant,
- color,
- weight,
- className,
- clickable: Boolean(onClick)
- }),
- onClick,
- ...rest
- }
- )
+ ({
+ color = "primary",
+ weight = "normal",
+ className,
+ onClick,
+ markdownEnabled = false,
+ children,
+ ...rest
+ }, ref) => {
+ const markdownHtml = markdownEnabled && typeof children === "string" ? tachTypographyMarkdownToHtml(children) : void 0;
+ const renderedChildren = markdownHtml ? /* @__PURE__ */ jsx("span", { dangerouslySetInnerHTML: { __html: markdownHtml } }) : children;
+ const contentProps = { children: renderedChildren };
+ return /* @__PURE__ */ jsx(
+ Component,
+ {
+ ref,
+ className: tachTypographyClassName({
+ variant,
+ color,
+ weight,
+ className,
+ clickable: Boolean(onClick)
+ }),
+ onClick,
+ ...rest,
+ ...contentProps
+ }
+ );
+ }
);
Variant.displayName = String(variant);
return Variant;
diff --git a/packages/tach-typography/dist/react/index.js.map b/packages/tach-typography/dist/react/index.js.map
index a4e029b..c3d6fbf 100644
--- a/packages/tach-typography/dist/react/index.js.map
+++ b/packages/tach-typography/dist/react/index.js.map
@@ -1 +1 @@
-{"version":3,"sources":["../../src/core/classnames.ts","../../src/react/index.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { TypographyClassOptions } from \"./types\";\n\nconst BASE_CLASS = \"tach-typography\";\n\nconst join = (...parts: Array${escapeHtml(code)}`,\n );\n return token;\n })\n .replace(/\\[([^\\]\\n]+)\\]\\(([^)\\n]+)\\)/g, (_match, label: string, hrefRaw: string) => {\n const token = `${TOKEN_PREFIX}${tokenId++}`;\n const href = sanitizeHref(hrefRaw);\n\n if (!href) {\n tokenMap.set(token, escapeHtml(label));\n return token;\n }\n\n tokenMap.set(\n token,\n `${escapeHtml(label)}`,\n );\n return token;\n });\n\n let html = escapeHtml(tokenized)\n .replace(/\\*\\*([^*\\n]+)\\*\\*/g, \"$1\")\n .replace(/__([^_\\n]+)__/g, \"$1\")\n .replace(/\\*([^*\\n]+)\\*/g, \"$1\")\n .replace(/_([^_\\n]+)_/g, \"$1\")\n .replace(/~~([^~\\n]+)~~/g, \"$1\")\n .replace(/\\n/g, \"
\");\n\n html = html.replace(new RegExp(`${TOKEN_PREFIX}\\\\d+`, \"g\"), token => tokenMap.get(token) || token);\n\n return html;\n};\n","import React from \"react\";\n\nimport { Typography } from \"antd\";\nimport type { LinkProps } from \"antd/lib/typography/Link\";\nimport type { ParagraphProps } from \"antd/lib/typography/Paragraph\";\nimport type { TextProps } from \"antd/lib/typography/Text\";\nimport type { TitleProps } from \"antd/lib/typography/Title\";\n\nimport {\n tachTypographyMarkdownToHtml,\n tachTypographyClassName,\n type TypographyColor,\n type TypographyVariant,\n type TypographyWeight,\n} from \"../core\";\n\ninterface AdditionalProps {\n color?: TypographyColor;\n weight?: TypographyWeight;\n onClick?: (event: React.MouseEvent
-
-
-
-
- strike code",
+ );
+ });
+
+ it("sanitizes html", () => {
+ expect(tachTypographyMarkdownToHtml("")).toBe(
+ "<script>alert(1)</script>",
+ );
+ });
+
+ it("keeps only safe link protocols", () => {
+ expect(
+ tachTypographyMarkdownToHtml("[safe](https://example.com) [unsafe](javascript:alert-1)"),
+ ).toBe(
+ "safe unsafe",
+ );
+ });
+});
diff --git a/packages/tach-typography/src/core/index.ts b/packages/tach-typography/src/core/index.ts
index 804f5ac..81f20dc 100644
--- a/packages/tach-typography/src/core/index.ts
+++ b/packages/tach-typography/src/core/index.ts
@@ -1,3 +1,4 @@
export * from "./types";
export * from "./classnames";
export * from "./ellipsis";
+export * from "./markdown";
diff --git a/packages/tach-typography/src/core/markdown.ts b/packages/tach-typography/src/core/markdown.ts
new file mode 100644
index 0000000..a42b810
--- /dev/null
+++ b/packages/tach-typography/src/core/markdown.ts
@@ -0,0 +1,65 @@
+const TOKEN_PREFIX = "TACHMDTOKEN";
+const SAFE_HREF_PATTERN = /^(https?:|mailto:|tel:|\/|#)/i;
+
+const escapeHtml = (value: string): string =>
+ value
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+
+const normalizeMarkdown = (value: string): string => value.replace(/\r\n?/g, "\n");
+
+const sanitizeHref = (value: string): string | null => {
+ const href = value.trim();
+
+ if (!href || !SAFE_HREF_PATTERN.test(href)) {
+ return null;
+ }
+
+ return href;
+};
+
+export const tachTypographyMarkdownToHtml = (markdown: string): string => {
+ const source = normalizeMarkdown(markdown);
+ const tokenMap = new Map${escapeHtml(code)}`,
+ );
+ return token;
+ })
+ .replace(/\[([^\]\n]+)\]\(([^)\n]+)\)/g, (_match, label: string, hrefRaw: string) => {
+ const token = `${TOKEN_PREFIX}${tokenId++}`;
+ const href = sanitizeHref(hrefRaw);
+
+ if (!href) {
+ tokenMap.set(token, escapeHtml(label));
+ return token;
+ }
+
+ tokenMap.set(
+ token,
+ `${escapeHtml(label)}`,
+ );
+ return token;
+ });
+
+ let html = escapeHtml(tokenized)
+ .replace(/\*\*([^*\n]+)\*\*/g, "$1")
+ .replace(/__([^_\n]+)__/g, "$1")
+ .replace(/\*([^*\n]+)\*/g, "$1")
+ .replace(/_([^_\n]+)_/g, "$1")
+ .replace(/~~([^~\n]+)~~/g, "$1")
+ .replace(/\n/g, "
");
+
+ html = html.replace(new RegExp(`${TOKEN_PREFIX}\\d+`, "g"), token => tokenMap.get(token) || token);
+
+ return html;
+};
diff --git a/packages/tach-typography/src/react/index.tsx b/packages/tach-typography/src/react/index.tsx
index e93c7e0..9c5f0ca 100644
--- a/packages/tach-typography/src/react/index.tsx
+++ b/packages/tach-typography/src/react/index.tsx
@@ -7,6 +7,7 @@ import type { TextProps } from "antd/lib/typography/Text";
import type { TitleProps } from "antd/lib/typography/Title";
import {
+ tachTypographyMarkdownToHtml,
tachTypographyClassName,
type TypographyColor,
type TypographyVariant,
@@ -18,6 +19,8 @@ interface AdditionalProps {
weight?: TypographyWeight;
onClick?: (event: React.MouseEventAngular usage
+
+
+
+{`
+ tachTypographyClassName
Merges with token classes
+
+
+ markdownEnabled
+ boolean
+ markdownEnabled
+ [markdownEnabled]
+ Enables markdown rendering from content string
+
+
diff --git a/packages/tach-typography/src/styles/tach-typography.css b/packages/tach-typography/src/styles/tach-typography.css
index 341acfd..8fecee7 100644
--- a/packages/tach-typography/src/styles/tach-typography.css
+++ b/packages/tach-typography/src/styles/tach-typography.css
@@ -88,6 +88,17 @@
.tach-typography--AppMediumSubtextUnderline,
.ant-typography.tach-typography--AppMediumSubtextUnderline { font-family: Inter, sans-serif; font-size: 11px; font-weight: 400; line-height: 17px; text-decoration: underline; }
+.tach-typography .tach-typography__md-link,
+.ant-typography.tach-typography .tach-typography__md-link {
+ color: inherit;
+ text-decoration: underline;
+}
+
+.tach-typography .tach-typography__md-code,
+.ant-typography.tach-typography .tach-typography__md-code {
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
+}
+
@media (max-width: 575px) {
.tach-typography--AccentLargeTtl,
.ant-typography.tach-typography--AccentLargeTtl {
markdown
+ string
+ children string
+ [markdown]
+ Angular markdown source string
+