chore: Монорепо с общими пакетами

This commit is contained in:
2026-03-04 16:31:57 +03:00
parent 8f2c799235
commit 915c56351b
420 changed files with 13403 additions and 7071 deletions

View File

@@ -0,0 +1,140 @@
import { NgModule } from "@angular/core";
import { Directive, ElementRef, Input, OnChanges, Renderer2, SimpleChanges } from "@angular/core";
import { NzTypographyModule } from "ng-zorro-antd/typography";
import {
tachTypographyClassList,
tachTypographyClassName,
tachTypographyEllipsisStyle,
type EllipsisOptions,
type TypographyClassOptions,
type TypographyColor,
type TypographyRenderOptions,
type TypographyVariant,
type TypographyWeight,
} from "../core";
export type AngularTypographyClassInput = TypographyClassOptions;
export interface AngularTypographyRenderOptions extends TypographyRenderOptions {
preserveStyle?: Record<string, string | number>;
}
const camelToKebab = (value: string): string =>
value.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`);
const toCssProperty = (styleKey: string): string => {
if (styleKey.startsWith("Webkit")) {
return `-webkit-${camelToKebab(styleKey.slice(6))}`;
}
return camelToKebab(styleKey);
};
export const tachAngularTypographyClassName = (
options: AngularTypographyClassInput = {},
): string => {
return tachTypographyClassName(options);
};
export const tachAngularTypographyClassList = (
options: AngularTypographyClassInput = {},
): string[] => {
return tachTypographyClassList(options);
};
export const tachAngularTypographyStyles = (
ellipsis?: EllipsisOptions,
preserveStyle: Record<string, string | number> = {},
): Record<string, string | number> => {
const ellipsisStyle = tachTypographyEllipsisStyle(ellipsis);
if (!ellipsisStyle) {
return preserveStyle;
}
return {
...ellipsisStyle,
...preserveStyle,
};
};
@Directive({
selector: "[tachTypography]",
standalone: true,
})
export class TachTypographyDirective implements OnChanges {
@Input() tachTypography: TypographyVariant | "" | undefined;
@Input() tachTypographyVariant: TypographyVariant = "Body";
@Input() tachTypographyColor: TypographyColor = "primary";
@Input() tachTypographyWeight: TypographyWeight = "normal";
@Input() tachTypographyClickable = false;
@Input() tachTypographyClassName: string | undefined;
@Input() tachTypographyEllipsis: EllipsisOptions | undefined;
private readonly appliedClasses = new Set<string>();
private readonly appliedStyleProperties = new Set<string>();
constructor(
private readonly elementRef: ElementRef<HTMLElement>,
private readonly renderer: Renderer2,
) {}
ngOnChanges(_changes: SimpleChanges): void {
this.syncClasses();
this.syncEllipsisStyles();
}
private syncClasses(): void {
const nextClassList = tachTypographyClassList({
variant: this.tachTypography || this.tachTypographyVariant,
color: this.tachTypographyColor,
weight: this.tachTypographyWeight,
clickable: this.tachTypographyClickable,
className: this.tachTypographyClassName,
});
const nextSet = new Set(nextClassList);
for (const className of this.appliedClasses) {
if (!nextSet.has(className)) {
this.renderer.removeClass(this.elementRef.nativeElement, className);
}
}
for (const className of nextSet) {
this.renderer.addClass(this.elementRef.nativeElement, className);
}
this.appliedClasses.clear();
for (const className of nextSet) {
this.appliedClasses.add(className);
}
}
private syncEllipsisStyles(): void {
const nextStyles = tachTypographyEllipsisStyle(this.tachTypographyEllipsis) || {};
const nextStyleKeys = new Set(Object.keys(nextStyles));
for (const styleKey of this.appliedStyleProperties) {
if (!nextStyleKeys.has(styleKey)) {
this.renderer.removeStyle(this.elementRef.nativeElement, toCssProperty(styleKey));
}
}
for (const [styleKey, styleValue] of Object.entries(nextStyles)) {
this.renderer.setStyle(this.elementRef.nativeElement, toCssProperty(styleKey), styleValue);
}
this.appliedStyleProperties.clear();
for (const styleKey of nextStyleKeys) {
this.appliedStyleProperties.add(styleKey);
}
}
}
@NgModule({
imports: [NzTypographyModule, TachTypographyDirective],
exports: [NzTypographyModule, TachTypographyDirective],
})
export class TachTypographyNzModule {}

View File

@@ -0,0 +1,29 @@
import type { TypographyClassOptions } from "./types";
const BASE_CLASS = "tach-typography";
const join = (...parts: Array<string | undefined | null | false>): string =>
parts.filter(Boolean).join(" ");
export const tachTypographyClassName = ({
variant = "Body",
color = "primary",
weight = "normal",
clickable = false,
className,
}: TypographyClassOptions = {}): string => {
return join(
BASE_CLASS,
`${BASE_CLASS}--${variant}`,
`${BASE_CLASS}--color-${color}`,
weight === "bold" && `${BASE_CLASS}--bold`,
clickable && `${BASE_CLASS}--pointer`,
className,
);
};
export const tachTypographyClassList = (options: TypographyClassOptions = {}): string[] => {
return tachTypographyClassName(options)
.split(" ")
.filter(Boolean);
};

View File

@@ -0,0 +1,21 @@
import type { EllipsisOptions } from "./types";
type StyleRecord = Record<string, string | number>;
export const tachTypographyEllipsisStyle = (
ellipsis?: EllipsisOptions,
): StyleRecord | undefined => {
if (!ellipsis) {
return undefined;
}
const rows = typeof ellipsis === "object" ? ellipsis.rows ?? 1 : 1;
return {
overflow: "hidden",
textOverflow: "ellipsis",
display: "-webkit-box",
WebkitBoxOrient: "vertical",
WebkitLineClamp: rows,
};
};

View File

@@ -0,0 +1,56 @@
import { describe, expect, it } from "vitest";
import {
tachTypographyClassList,
tachTypographyClassName,
tachTypographyEllipsisStyle,
} from "./index";
describe("tachTypographyClassName", () => {
it("builds className string with defaults", () => {
expect(tachTypographyClassName()).toBe(
"tach-typography tach-typography--Body tach-typography--color-primary",
);
});
it("adds optional states", () => {
expect(
tachTypographyClassName({
variant: "AccentH1",
color: "link",
weight: "bold",
clickable: true,
className: "custom",
}),
).toBe(
"tach-typography tach-typography--AccentH1 tach-typography--color-link tach-typography--bold tach-typography--pointer custom",
);
});
it("returns list helper", () => {
expect(tachTypographyClassList({ variant: "Title1" })).toEqual([
"tach-typography",
"tach-typography--Title1",
"tach-typography--color-primary",
]);
});
});
describe("tachTypographyEllipsisStyle", () => {
it("returns undefined without ellipsis", () => {
expect(tachTypographyEllipsisStyle()).toBeUndefined();
});
it("returns one-line style for true", () => {
expect(tachTypographyEllipsisStyle(true)).toMatchObject({
WebkitLineClamp: 1,
overflow: "hidden",
});
});
it("respects rows value", () => {
expect(tachTypographyEllipsisStyle({ rows: 3 })).toMatchObject({
WebkitLineClamp: 3,
});
});
});

View File

@@ -0,0 +1,3 @@
export * from "./types";
export * from "./classnames";
export * from "./ellipsis";

View File

@@ -0,0 +1,61 @@
export const TYPOGRAPHY_VARIANTS = [
"LargeTitle",
"Title1",
"Title2",
"Title3",
"Headline",
"Body",
"Inputs",
"Subheadline",
"FootnoteUnderline",
"Footnote",
"Caption",
"Caption2",
"AccentH1",
"AccentH2",
"AccentSubttl",
"AccentSubttl2",
"AccentCaption",
"AccentCaption2",
"AccentRegularM",
"AccentRegularS",
"AccentLargeTtl",
"AppMediumBody",
"AppMediumSubtext",
"AppMediumSubtextUnderline",
] as const;
export const TYPOGRAPHY_COLORS = [
"primary",
"secondary",
"tertiary",
"quaternary",
"link",
"white",
"dark",
"alert",
"malahit",
"attantion",
] as const;
export type TypographyVariant = (typeof TYPOGRAPHY_VARIANTS)[number];
export type TypographyColor = (typeof TYPOGRAPHY_COLORS)[number];
export type TypographyWeight = "normal" | "bold";
export interface TypographyClassOptions {
variant?: TypographyVariant;
color?: TypographyColor;
weight?: TypographyWeight;
clickable?: boolean;
className?: string | undefined;
}
export type EllipsisOptions =
| boolean
| {
rows?: number;
};
export interface TypographyRenderOptions extends TypographyClassOptions {
ellipsis?: EllipsisOptions;
}

View File

@@ -0,0 +1,82 @@
import React from "react";
import { Typography } from "antd";
import type { LinkProps } from "antd/lib/typography/Link";
import type { ParagraphProps } from "antd/lib/typography/Paragraph";
import type { TextProps } from "antd/lib/typography/Text";
import type { TitleProps } from "antd/lib/typography/Title";
import {
tachTypographyClassName,
type TypographyColor,
type TypographyVariant,
type TypographyWeight,
} from "../core";
interface AdditionalProps {
color?: TypographyColor;
weight?: TypographyWeight;
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
className?: string | undefined;
}
const createTypographyVariant = <P extends object>(
Component: React.ComponentType<P>,
variant: TypographyVariant,
) => {
const Variant = React.forwardRef<HTMLElement, P & AdditionalProps>(
({ color = "primary", weight = "normal", className, onClick, ...rest }, ref) => (
<Component
ref={ref as never}
className={tachTypographyClassName({
variant,
color,
weight,
className,
clickable: Boolean(onClick),
})}
onClick={onClick}
{...(rest as P)}
/>
),
);
Variant.displayName = String(variant);
return Variant;
};
const createTypographyComponent = <P extends object>(Component: React.ComponentType<P>) => ({
LargeTitle: createTypographyVariant(Component, "LargeTitle"),
Title1: createTypographyVariant(Component, "Title1"),
Title2: createTypographyVariant(Component, "Title2"),
Title3: createTypographyVariant(Component, "Title3"),
Headline: createTypographyVariant(Component, "Headline"),
Body: createTypographyVariant(Component, "Body"),
Inputs: createTypographyVariant(Component, "Inputs"),
Subheadline: createTypographyVariant(Component, "Subheadline"),
FootnoteUnderline: createTypographyVariant(Component, "FootnoteUnderline"),
Footnote: createTypographyVariant(Component, "Footnote"),
Caption: createTypographyVariant(Component, "Caption"),
Caption2: createTypographyVariant(Component, "Caption2"),
AccentH1: createTypographyVariant(Component, "AccentH1"),
AccentH2: createTypographyVariant(Component, "AccentH2"),
AccentSubttl: createTypographyVariant(Component, "AccentSubttl"),
AccentSubttl2: createTypographyVariant(Component, "AccentSubttl2"),
AccentCaption: createTypographyVariant(Component, "AccentCaption"),
AccentCaption2: createTypographyVariant(Component, "AccentCaption2"),
AccentRegularM: createTypographyVariant(Component, "AccentRegularM"),
AccentRegularS: createTypographyVariant(Component, "AccentRegularS"),
AccentLargeTtl: createTypographyVariant(Component, "AccentLargeTtl"),
AppMediumBody: createTypographyVariant(Component, "AppMediumBody"),
AppMediumSubtext: createTypographyVariant(Component, "AppMediumSubtext"),
AppMediumSubtextUnderline: createTypographyVariant(Component, "AppMediumSubtextUnderline"),
});
export const TachTypography = {
Text: createTypographyComponent<TextProps & Pick<ParagraphProps, "ellipsis">>(Typography.Text),
Paragraph: createTypographyComponent<ParagraphProps>(Typography.Paragraph),
Link: createTypographyComponent<LinkProps>(Typography.Link),
Title: createTypographyComponent<TitleProps>(Typography.Title),
};

View File

@@ -0,0 +1,183 @@
import React from "react";
import type { Meta, StoryObj } from "@storybook/react";
import { expect, fn, userEvent, within } from "@storybook/test";
import {
TYPOGRAPHY_COLORS,
TYPOGRAPHY_VARIANTS,
type TypographyColor,
type TypographyVariant,
type TypographyWeight,
} from "../core";
import { TachTypography } from "../react";
type TypographyNamespace = keyof typeof TachTypography;
type VariantComponentProps = {
children?: React.ReactNode;
className?: string;
color?: TypographyColor;
ellipsis?: boolean | { rows?: number };
href?: string;
level?: 1 | 2 | 3 | 4 | 5;
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
weight?: TypographyWeight;
};
type VariantComponent = React.ComponentType<VariantComponentProps>;
const getVariantComponent = (
namespace: TypographyNamespace,
variant: TypographyVariant,
): VariantComponent => {
return (TachTypography[namespace] as unknown as Record<TypographyVariant, VariantComponent>)[variant];
};
interface PlaygroundArgs {
namespace: TypographyNamespace;
variant: TypographyVariant;
color: TypographyColor;
weight: TypographyWeight;
children: string;
clickable: boolean;
ellipsisRows: number;
href: string;
titleLevel: 1 | 2 | 3 | 4 | 5;
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
}
const renderTypography = (args: PlaygroundArgs) => {
const Component = getVariantComponent(args.namespace, args.variant);
const componentProps: VariantComponentProps = {
color: args.color,
weight: args.weight,
};
if (args.clickable && args.onClick) {
componentProps.onClick = (event: React.MouseEvent<HTMLElement>) => {
args.onClick?.(event);
};
}
if (args.namespace === "Text" || args.namespace === "Paragraph") {
componentProps.ellipsis = args.ellipsisRows > 0 ? { rows: args.ellipsisRows } : false;
}
if (args.namespace === "Link") {
componentProps.href = args.href;
}
if (args.namespace === "Title") {
componentProps.level = args.titleLevel;
}
return (
<div className="tach-story-surface">
<Component {...componentProps}>{args.children}</Component>
</div>
);
};
const meta: Meta<PlaygroundArgs> = {
title: "TachTypography/Playground",
tags: ["autodocs"],
render: renderTypography,
args: {
namespace: "Text",
variant: "Body",
color: "primary",
weight: "normal",
children: "TachTypography playground text",
clickable: false,
ellipsisRows: 0,
href: "https://example.com",
titleLevel: 3,
},
argTypes: {
namespace: {
control: "select",
options: ["Text", "Paragraph", "Link", "Title"],
},
variant: {
control: "select",
options: TYPOGRAPHY_VARIANTS,
},
color: {
control: "select",
options: TYPOGRAPHY_COLORS,
},
weight: {
control: "inline-radio",
options: ["normal", "bold"],
},
children: {
control: "text",
},
clickable: {
control: "boolean",
},
ellipsisRows: {
control: {
type: "number",
min: 0,
max: 5,
},
description: "Works for Text and Paragraph namespaces",
},
href: {
control: "text",
description: "Works for Link namespace",
},
titleLevel: {
control: "inline-radio",
options: [1, 2, 3, 4, 5],
description: "Works for Title namespace",
},
onClick: {
table: {
disable: true,
},
},
},
parameters: {
docs: {
description: {
component:
"Interactive playground for all TachTypography namespaces with full token and prop controls.",
},
},
},
};
export default meta;
type Story = StoryObj<typeof meta>;
export const Interactive: Story = {};
export const WithEllipsis: Story = {
args: {
namespace: "Paragraph",
variant: "Body",
ellipsisRows: 2,
children:
"This paragraph demonstrates multi-line truncation in Storybook. Increase or decrease ellipsisRows to validate visual behavior and clipping boundaries.",
},
};
export const ClickInteraction: Story = {
args: {
namespace: "Text",
variant: "Subheadline",
clickable: true,
children: "Click this text to run interaction assertion",
onClick: fn(),
},
play: async ({ canvasElement, args }) => {
const canvas = within(canvasElement);
await userEvent.click(canvas.getByText(args.children));
await expect(args.onClick as ReturnType<typeof fn>).toHaveBeenCalledTimes(1);
},
};

View File

@@ -0,0 +1,105 @@
import React from "react";
import type { Meta, StoryObj } from "@storybook/react";
const meta = {
title: "TachTypography/Reference",
tags: ["autodocs"],
} satisfies Meta;
export default meta;
type Story = StoryObj<typeof meta>;
export const PropsMatrix: Story = {
render: () => (
<div className="tach-story-surface tach-story-stack">
<table className="tach-story-table">
<thead>
<tr>
<th>Prop</th>
<th>Type</th>
<th>React</th>
<th>Angular</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td>variant</td>
<td>TypographyVariant</td>
<td>TachTypography.[Text|Paragraph|Link|Title].Variant</td>
<td>tachTypography / tachTypographyVariant</td>
<td>Main typography token</td>
</tr>
<tr>
<td>color</td>
<td>TypographyColor</td>
<td>color</td>
<td>tachTypographyColor</td>
<td>Maps to CSS variables</td>
</tr>
<tr>
<td>weight</td>
<td>"normal" | "bold"</td>
<td>weight</td>
<td>tachTypographyWeight</td>
<td>Bold class modifier</td>
</tr>
<tr>
<td>ellipsis</td>
<td><code>{"boolean | { rows?: number }"}</code></td>
<td>ellipsis (Text/Paragraph)</td>
<td>tachTypographyEllipsis</td>
<td>Applies line clamp styles</td>
</tr>
<tr>
<td>clickable</td>
<td>boolean</td>
<td>onClick adds pointer class</td>
<td>tachTypographyClickable</td>
<td>Visual affordance + cursor</td>
</tr>
<tr>
<td>className</td>
<td>string</td>
<td>className</td>
<td>tachTypographyClassName</td>
<td>Merges with token classes</td>
</tr>
</tbody>
</table>
</div>
),
};
export const AngularAdapter: Story = {
render: () => (
<div className="tach-story-surface tach-story-stack">
<h4>Angular adapter usage</h4>
<pre>
<code>
{`import { TachTypographyDirective, TachTypographyNzModule } from "@hublib-web/tach-typography/angular";
@Component({
standalone: true,
imports: [TachTypographyNzModule, TachTypographyDirective],
template: \
\`<span
nz-typography
tachTypography
tachTypography="Body"
tachTypographyColor="link"
tachTypographyWeight="bold"
[tachTypographyEllipsis]="{ rows: 2 }"
>
Typography for Angular + NG-ZORRO
</span>\`,
})
export class ExampleComponent {}
`}
</code>
</pre>
</div>
),
};

View File

@@ -0,0 +1,189 @@
import React from "react";
import type { Meta, StoryObj } from "@storybook/react";
import {
TYPOGRAPHY_COLORS,
TYPOGRAPHY_VARIANTS,
type TypographyColor,
type TypographyVariant,
type TypographyWeight,
} from "../core";
import { TachTypography } from "../react";
type TypographyNamespace = keyof typeof TachTypography;
type VariantComponentProps = {
children?: React.ReactNode;
color?: TypographyColor;
ellipsis?: boolean | { rows?: number };
href?: string;
level?: 1 | 2 | 3 | 4 | 5;
weight?: TypographyWeight;
};
type VariantComponent = React.ComponentType<VariantComponentProps>;
const getVariantComponent = (
namespace: TypographyNamespace,
variant: TypographyVariant,
): VariantComponent => {
return (TachTypography[namespace] as unknown as Record<TypographyVariant, VariantComponent>)[variant];
};
interface VariantScaleArgs {
namespace: TypographyNamespace;
color: TypographyColor;
weight: TypographyWeight;
sampleText: string;
}
interface ColorPaletteArgs {
namespace: TypographyNamespace;
variant: TypographyVariant;
weight: TypographyWeight;
}
const meta = {
title: "TachTypography/Tokens",
tags: ["autodocs"],
parameters: {
docs: {
description: {
component:
"Token showcase for typography variants and colors. Useful for validating visual consistency against the design system.",
},
},
},
} satisfies Meta;
export default meta;
type Story = StoryObj<typeof meta>;
export const VariantScale: StoryObj<VariantScaleArgs> = {
args: {
namespace: "Text",
color: "primary",
weight: "normal",
sampleText: "The quick brown fox jumps over the lazy dog",
},
argTypes: {
namespace: {
control: "select",
options: ["Text", "Paragraph", "Link", "Title"],
},
color: {
control: "select",
options: TYPOGRAPHY_COLORS,
},
weight: {
control: "inline-radio",
options: ["normal", "bold"],
},
sampleText: {
control: "text",
},
},
render: args => (
<div className="tach-story-surface tach-story-stack">
{TYPOGRAPHY_VARIANTS.map(variant => {
const Component = getVariantComponent(args.namespace, variant);
const componentProps: VariantComponentProps = {
color: args.color,
weight: args.weight,
};
if (args.namespace === "Link") {
componentProps.href = "https://example.com";
}
if (args.namespace === "Title") {
componentProps.level = 4;
}
return (
<div className="tach-story-row" key={variant}>
<span className="tach-story-label">{variant}</span>
<Component {...componentProps}>{args.sampleText}</Component>
</div>
);
})}
</div>
),
};
export const ColorPalette: StoryObj<ColorPaletteArgs> = {
args: {
namespace: "Text",
variant: "Body",
weight: "normal",
},
argTypes: {
namespace: {
control: "select",
options: ["Text", "Paragraph", "Link", "Title"],
},
variant: {
control: "select",
options: TYPOGRAPHY_VARIANTS,
},
weight: {
control: "inline-radio",
options: ["normal", "bold"],
},
},
render: args => {
const Component = getVariantComponent(args.namespace, args.variant);
return (
<div className="tach-story-surface tach-story-grid tach-story-grid--colors">
{TYPOGRAPHY_COLORS.map(color => {
const componentProps: VariantComponentProps = {
color,
weight: args.weight,
};
if (args.namespace === "Link") {
componentProps.href = "https://example.com";
}
if (args.namespace === "Title") {
componentProps.level = 4;
}
return (
<div className="tach-story-row" key={color}>
<span className="tach-story-label">{color}</span>
<Component {...componentProps}>Color token preview</Component>
</div>
);
})}
</div>
);
},
};
export const TokenReference: Story = {
render: () => (
<div className="tach-story-surface tach-story-stack">
<div>
<h4>Variants ({TYPOGRAPHY_VARIANTS.length})</h4>
<ul className="tach-story-token-list">
{TYPOGRAPHY_VARIANTS.map(token => (
<li key={token}>{token}</li>
))}
</ul>
</div>
<div>
<h4>Colors ({TYPOGRAPHY_COLORS.length})</h4>
<ul className="tach-story-token-list">
{TYPOGRAPHY_COLORS.map(token => (
<li key={token}>{token}</li>
))}
</ul>
</div>
</div>
),
};

View File

@@ -0,0 +1,103 @@
.tach-typography {
margin: 0;
}
.ant-typography.tach-typography {
margin-top: 0 !important;
margin-bottom: 0 !important;
}
.tach-typography--pointer,
.ant-typography.tach-typography--pointer {
cursor: pointer;
}
.tach-typography--bold,
.ant-typography.tach-typography--bold {
font-weight: 700 !important;
}
.tach-typography--color-primary,
.ant-typography.tach-typography--color-primary { color: var(--Text-Primary); }
.tach-typography--color-secondary,
.ant-typography.tach-typography--color-secondary { color: var(--Text-Secondary); }
.tach-typography--color-tertiary,
.ant-typography.tach-typography--color-tertiary { color: var(--Text-Tertiary); }
.tach-typography--color-quaternary,
.ant-typography.tach-typography--color-quaternary { color: var(--Text-Quaternary); }
.tach-typography--color-link,
.ant-typography.tach-typography--color-link { color: var(--System-HashtagsInPost); }
.tach-typography--color-white,
.ant-typography.tach-typography--color-white { color: var(--Default-White); }
.tach-typography--color-dark,
.ant-typography.tach-typography--color-dark { color: var(--Default-Dark); }
.tach-typography--color-alert,
.ant-typography.tach-typography--color-alert { color: var(--System-Alert); }
.tach-typography--color-malahit,
.ant-typography.tach-typography--color-malahit { color: var(--Accent-Malahit); }
.tach-typography--color-attantion,
.ant-typography.tach-typography--color-attantion { color: var(--System-Attantion); }
.tach-typography--LargeTitle,
.ant-typography.tach-typography--LargeTitle { font-family: Inter, sans-serif; font-size: 38px; font-weight: 500; line-height: 46px; }
.tach-typography--Title1,
.ant-typography.tach-typography--Title1 { font-family: Inter, sans-serif; font-size: 28px; font-weight: 500; line-height: 34px; }
.tach-typography--Title2,
.ant-typography.tach-typography--Title2 { font-family: Inter, sans-serif; font-size: 22px; font-weight: 500; line-height: 28px; }
.tach-typography--Title3,
.ant-typography.tach-typography--Title3 { font-family: Inter, sans-serif; font-size: 20px; font-weight: 500; line-height: 26px; }
.tach-typography--Headline,
.ant-typography.tach-typography--Headline { font-family: Inter, sans-serif; font-size: 16px; font-weight: 500; line-height: 24px; }
.tach-typography--Body,
.tach-typography--AppMediumBody,
.ant-typography.tach-typography--Body,
.ant-typography.tach-typography--AppMediumBody { font-family: Inter, sans-serif; font-size: 14px; font-weight: 500; line-height: 20px; }
.tach-typography--Inputs,
.ant-typography.tach-typography--Inputs { font-family: Inter, sans-serif; font-size: 14px; font-weight: 500; line-height: 24px; }
.tach-typography--Subheadline,
.ant-typography.tach-typography--Subheadline { font-family: Inter, sans-serif; font-size: 14px; font-weight: 500; line-height: 18px; }
.tach-typography--FootnoteUnderline,
.ant-typography.tach-typography--FootnoteUnderline { font-family: Inter, sans-serif; font-size: 13px; font-weight: 500; line-height: 18px; text-decoration: underline; }
.tach-typography--Footnote,
.ant-typography.tach-typography--Footnote { font-family: Inter, sans-serif; font-size: 13px; font-weight: 500; line-height: 18px; }
.tach-typography--Caption,
.ant-typography.tach-typography--Caption { font-family: Inter, sans-serif; font-size: 10px; font-weight: 500; line-height: 12px; text-transform: uppercase; }
.tach-typography--Caption2,
.ant-typography.tach-typography--Caption2 { font-family: Inter, sans-serif; font-size: 8px; font-weight: 500; line-height: 10px; text-transform: uppercase; }
.tach-typography--AccentH1,
.ant-typography.tach-typography--AccentH1 { font-family: Unbounded, sans-serif; font-size: 20px; font-weight: 700; line-height: 30px; }
.tach-typography--AccentH2,
.ant-typography.tach-typography--AccentH2 { font-family: Unbounded, sans-serif; font-size: 16px; font-weight: 700; line-height: 24px; }
.tach-typography--AccentSubttl,
.ant-typography.tach-typography--AccentSubttl { font-family: Unbounded, sans-serif; font-size: 14px; font-weight: 700; line-height: 22px; }
.tach-typography--AccentSubttl2,
.ant-typography.tach-typography--AccentSubttl2 { font-family: Unbounded, sans-serif; font-size: 12px; font-weight: 700; line-height: 20px; }
.tach-typography--AccentCaption,
.ant-typography.tach-typography--AccentCaption { font-family: Unbounded, sans-serif; font-size: 9px; font-weight: 700; line-height: 12px; text-transform: uppercase; }
.tach-typography--AccentCaption2,
.ant-typography.tach-typography--AccentCaption2 { font-family: Unbounded, sans-serif; font-size: 7px; font-weight: 700; line-height: 10px; text-transform: uppercase; }
.tach-typography--AccentRegularM,
.ant-typography.tach-typography--AccentRegularM { font-family: Unbounded, sans-serif; font-size: 14px; font-weight: 400; line-height: 22px; }
.tach-typography--AccentRegularS,
.ant-typography.tach-typography--AccentRegularS { font-family: Unbounded, sans-serif; font-size: 12px; font-weight: 400; line-height: 20px; }
.tach-typography--AccentLargeTtl,
.ant-typography.tach-typography--AccentLargeTtl { font-family: Unbounded, sans-serif; font-size: 38px; font-weight: 700; line-height: 52px; }
.tach-typography--AppMediumSubtext,
.ant-typography.tach-typography--AppMediumSubtext { text-align: center; font-family: Inter, sans-serif; font-size: 11px; font-weight: 400; line-height: 17px; }
.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; }
@media (max-width: 575px) {
.tach-typography--AccentLargeTtl,
.ant-typography.tach-typography--AccentLargeTtl {
font-size: 20px;
line-height: 30px;
}
.tach-typography--AccentRegularM,
.ant-typography.tach-typography--AccentRegularM {
font-size: 12px;
line-height: 20px;
}
}