diff --git a/change/@adaptive-web-adaptive-ui-381becf1-b6b2-4333-9695-40c2a1c8877e.json b/change/@adaptive-web-adaptive-ui-381becf1-b6b2-4333-9695-40c2a1c8877e.json new file mode 100644 index 00000000..78731163 --- /dev/null +++ b/change/@adaptive-web-adaptive-ui-381becf1-b6b2-4333-9695-40c2a1c8877e.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Added support for generated type scale token values", + "packageName": "@adaptive-web/adaptive-ui", + "email": "47367562+bheston@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/adaptive-ui-explorer/src/app.ts b/packages/adaptive-ui-explorer/src/app.ts index 3dd1c59f..95bd7401 100644 --- a/packages/adaptive-ui-explorer/src/app.ts +++ b/packages/adaptive-ui-explorer/src/app.ts @@ -39,6 +39,7 @@ import { ControlPane } from "./components/control-pane/index.js"; import { LayerBackground } from "./components/layer-background/index.js"; import { PaletteGradient } from "./components/palette-gradient/palette-gradient.js"; import { SampleApp } from "./sample/index.js"; +import { typeRampScale, Typography } from "./typography/index.js"; import { State } from "./state.js"; ColorBlock; @@ -46,6 +47,7 @@ ControlPane; LayerBackground; PaletteGradient; SampleApp; +Typography; const colorBlockTemplate = html` ${repeat( @@ -85,6 +87,12 @@ const sampleTemplate = html` `; +const typographyTemplate = html` + + + +`; + const template = html`
@@ -241,6 +249,18 @@ export class App extends FASTElement { case "wcagContrastLevel": wcagContrastLevel.setValueFor(app.canvas, source.wcagContrastLevel); break; + case "typeScaleBaseSize": + typeRampScale.base.fontSize.setValueFor(app.canvas, `${source.typeScaleBaseSize}px`); + break; + case "typeScaleMultiplier": + typeRampScale.multiplier.setValueFor(app.canvas, source.typeScaleMultiplier); + break; + case "typeScaleLineHeightRatio": + typeRampScale.lineHeightRatio.setValueFor(app.canvas, source.typeScaleLineHeightRatio); + break; + case "typeScaleLineHeightSnap": + typeRampScale.lineHeightSnap.setValueFor(app.canvas, `${source.typeScaleLineHeightSnap}px`); + break; default: break; } @@ -266,6 +286,8 @@ export class App extends FASTElement { public componentTypeTemplate(): ViewTemplate { if (this.state.componentType === ComponentType.sample) { return sampleTemplate; + } else if (this.state.componentType === ComponentType.typography) { + return typographyTemplate; } else { return colorBlockTemplate; } diff --git a/packages/adaptive-ui-explorer/src/component-type.ts b/packages/adaptive-ui-explorer/src/component-type.ts index 00ea6cb2..f2d8fcbd 100644 --- a/packages/adaptive-ui-explorer/src/component-type.ts +++ b/packages/adaptive-ui-explorer/src/component-type.ts @@ -2,5 +2,6 @@ export enum ComponentType { backplate = "backplate", text = "text", form = "form", + typography = "typography", sample = "sample", } diff --git a/packages/adaptive-ui-explorer/src/components/control-pane/control-pane.template.ts b/packages/adaptive-ui-explorer/src/components/control-pane/control-pane.template.ts index 9a079706..18e52abb 100644 --- a/packages/adaptive-ui-explorer/src/components/control-pane/control-pane.template.ts +++ b/packages/adaptive-ui-explorer/src/components/control-pane/control-pane.template.ts @@ -90,5 +90,66 @@ export function controlPaneTemplate(): ElementViewTemplat id="showSwatches" :checked=${twoWay((x) => x.state.showSwatches)} >Show swatches + +
+ + x.state.typeScaleBaseSize} + @input=${(x, c) => + x.state.typeScaleBaseSize = parseFloat(c.eventTarget().value) + } + /> +
+ +
+ + x.state.typeScaleMultiplier} + @input=${(x, c) => + x.state.typeScaleMultiplier = parseFloat(c.eventTarget().value) + } + /> +
+ +
+ + x.state.typeScaleLineHeightRatio} + @input=${(x, c) => + x.state.typeScaleLineHeightRatio = parseFloat(c.eventTarget().value) + } + /> +
+ +
+ + x.state.typeScaleLineHeightSnap} + @input=${(x, c) => + x.state.typeScaleLineHeightSnap = parseFloat(c.eventTarget().value) + } + /> +
+ + x.state.multiline)} + >Multiline typography `; } diff --git a/packages/adaptive-ui-explorer/src/state.ts b/packages/adaptive-ui-explorer/src/state.ts index 2de3ed1b..eeadccb5 100644 --- a/packages/adaptive-ui-explorer/src/state.ts +++ b/packages/adaptive-ui-explorer/src/state.ts @@ -14,6 +14,11 @@ export interface State { wcagContrastLevel: WcagContrastLevel; disabledState: boolean; showSwatches: boolean; + typeScaleBaseSize: number; + typeScaleMultiplier: number; + typeScaleLineHeightRatio: number; + typeScaleLineHeightSnap: number; + multiline: boolean; } const PLACEHOLDER_COLOR = Color.parse("#ff00ff")!; @@ -42,4 +47,19 @@ export class DefaultState implements State { @observable public showSwatches: boolean = false; + + @observable + public typeScaleBaseSize: number = 16; + + @observable + public typeScaleMultiplier: number = 1.2; + + @observable + public typeScaleLineHeightRatio: number = 1.4; + + @observable + public typeScaleLineHeightSnap: number = 2; + + @observable + public multiline: boolean = false; } diff --git a/packages/adaptive-ui-explorer/src/typography/index.ts b/packages/adaptive-ui-explorer/src/typography/index.ts new file mode 100644 index 00000000..95111fc7 --- /dev/null +++ b/packages/adaptive-ui-explorer/src/typography/index.ts @@ -0,0 +1,2 @@ +export { Typography } from "./typography.js"; +export { typeRampScale } from "./type-scale.js"; diff --git a/packages/adaptive-ui-explorer/src/typography/type-scale.ts b/packages/adaptive-ui-explorer/src/typography/type-scale.ts new file mode 100644 index 00000000..e244f0a1 --- /dev/null +++ b/packages/adaptive-ui-explorer/src/typography/type-scale.ts @@ -0,0 +1,3 @@ +import { TypeScaleTokenGroup } from "@adaptive-web/adaptive-ui"; + +export const typeRampScale = new TypeScaleTokenGroup("typography.ramp.scale", "16px", 1.2, 1.4, "2px"); diff --git a/packages/adaptive-ui-explorer/src/typography/typography.styles.ts b/packages/adaptive-ui-explorer/src/typography/typography.styles.ts new file mode 100644 index 00000000..b6e937a8 --- /dev/null +++ b/packages/adaptive-ui-explorer/src/typography/typography.styles.ts @@ -0,0 +1,85 @@ +import { css } from "@microsoft/fast-element"; +import { bodyFontFamily, colorContext, neutralFillSubtle, neutralStrokeStrong } from "@adaptive-web/adaptive-ui/reference"; +import { componentBaseStyles } from "@adaptive-web/adaptive-web-components"; +import { typeRampScale } from "./type-scale.js"; + +export const typographyStyles = css` + ${componentBaseStyles} + + :host { + display: flex; + font-family: ${bodyFontFamily}; + color: ${neutralStrokeStrong.rest}; + background: ${colorContext}; + } + + .content { + display: flex; + flex-direction: column; + gap: 16px; + padding: 24px; + } + + p { + margin: 0; + display: flex; + flex-direction: column; + gap: 4px; + } + + .sample-text { + display: block; + background: ${neutralFillSubtle.rest}; + } + + .values { + font-size: 14px; + line-height: 16px; + font-family: monospace; + } + + p.minus2 { + font-size: ${typeRampScale.minus2.fontSize}; + line-height: ${typeRampScale.minus2.lineHeight}; + } + + p.minus1 { + font-size: ${typeRampScale.minus1.fontSize}; + line-height: ${typeRampScale.minus1.lineHeight}; + } + + p.base { + font-size: ${typeRampScale.base.fontSize}; + line-height: ${typeRampScale.base.lineHeight}; + } + + p.plus1 { + font-size: ${typeRampScale.plus1.fontSize}; + line-height: ${typeRampScale.plus1.lineHeight}; + } + + p.plus2 { + font-size: ${typeRampScale.plus2.fontSize}; + line-height: ${typeRampScale.plus2.lineHeight}; + } + + p.plus3 { + font-size: ${typeRampScale.plus3.fontSize}; + line-height: ${typeRampScale.plus3.lineHeight}; + } + + p.plus4 { + font-size: ${typeRampScale.plus4.fontSize}; + line-height: ${typeRampScale.plus4.lineHeight}; + } + + p.plus5 { + font-size: ${typeRampScale.plus5.fontSize}; + line-height: ${typeRampScale.plus5.lineHeight}; + } + + p.plus6 { + font-size: ${typeRampScale.plus6.fontSize}; + line-height: ${typeRampScale.plus6.lineHeight}; + } +`; diff --git a/packages/adaptive-ui-explorer/src/typography/typography.template.ts b/packages/adaptive-ui-explorer/src/typography/typography.template.ts new file mode 100644 index 00000000..9dda9458 --- /dev/null +++ b/packages/adaptive-ui-explorer/src/typography/typography.template.ts @@ -0,0 +1,48 @@ +import { ElementViewTemplate, html } from "@microsoft/fast-element"; +import { Typography } from "./typography.js"; + +const singleLineText = html`Lorem ipsum dolor sit amet, consectetur adipiscing elit.`; +const multiLineText = html`Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Sed do eiusmod tempor incididunt ut labore et dolore.`; + +export function typographyTemplate(): ElementViewTemplate { + return html` +
+

+ Minus 2 (font-size: ${x => x.minus2.fontSize}, line-height: ${x => x.minus2.lineHeight}) + ${x => x.state.multiline ? multiLineText : singleLineText} +

+

+ Minus 1 (font-size: ${x => x.minus1.fontSize}, line-height: ${x => x.minus1.lineHeight}) + ${x => x.state.multiline ? multiLineText : singleLineText} +

+

+ Base (font-size: ${x => x.base.fontSize}, line-height: ${x => x.base.lineHeight}) + ${x => x.state.multiline ? multiLineText : singleLineText} +

+

+ Plus 1 (font-size: ${x => x.plus1.fontSize}, line-height: ${x => x.plus1.lineHeight}) + ${x => x.state.multiline ? multiLineText : singleLineText} +

+

+ Plus 2 (font-size: ${x => x.plus2.fontSize}, line-height: ${x => x.plus2.lineHeight}) + ${x => x.state.multiline ? multiLineText : singleLineText} +

+

+ Plus 3 (font-size: ${x => x.plus3.fontSize}, line-height: ${x => x.plus3.lineHeight}) + ${x => x.state.multiline ? multiLineText : singleLineText} +

+

+ Plus 4 (font-size: ${x => x.plus4.fontSize}, line-height: ${x => x.plus4.lineHeight}) + ${x => x.state.multiline ? multiLineText : singleLineText} +

+

+ Plus 5 (font-size: ${x => x.plus5.fontSize}, line-height: ${x => x.plus5.lineHeight}) + ${x => x.state.multiline ? multiLineText : singleLineText} +

+

+ Plus 6 (font-size: ${x => x.plus6.fontSize}, line-height: ${x => x.plus6.lineHeight}) + ${x => x.state.multiline ? multiLineText : singleLineText} +

+
+ `; +} diff --git a/packages/adaptive-ui-explorer/src/typography/typography.ts b/packages/adaptive-ui-explorer/src/typography/typography.ts new file mode 100644 index 00000000..f1416208 --- /dev/null +++ b/packages/adaptive-ui-explorer/src/typography/typography.ts @@ -0,0 +1,105 @@ +import { customElement, FASTElement, observable } from "@microsoft/fast-element"; +import { DesignToken, DesignTokenChangeRecord } from "@microsoft/fast-foundation"; +import { State } from "../state.js"; +import { typographyStyles as styles } from "./typography.styles.js"; +import { typographyTemplate as template } from "./typography.template.js"; +import { typeRampScale } from "./type-scale.js"; + +interface TypePosition { + fontSize: string; + lineHeight: string; +} + +@customElement({ + name: "app-typography", + template: template(), + styles, +}) +export class Typography extends FASTElement { + @State state!: State; + + @observable + minus2: TypePosition = { fontSize: "", lineHeight: "" }; + + @observable + minus1: TypePosition = { fontSize: "", lineHeight: "" }; + + @observable + base: TypePosition = { fontSize: "", lineHeight: "" }; + + @observable + plus1: TypePosition = { fontSize: "", lineHeight: "" }; + + @observable + plus2: TypePosition = { fontSize: "", lineHeight: "" }; + + @observable + plus3: TypePosition = { fontSize: "", lineHeight: "" }; + + @observable + plus4: TypePosition = { fontSize: "", lineHeight: "" }; + + @observable + plus5: TypePosition = { fontSize: "", lineHeight: "" }; + + @observable + plus6: TypePosition = { fontSize: "", lineHeight: "" }; + + private updateScheduled = false; + + connectedCallback(): void { + super.connectedCallback(); + + // Wait for next frame to ensure styles are applied + requestAnimationFrame(() => { + this.updateComputedValues(); + }); + + // Subscribe to token changes + this.subscribeToTokenChanges(); + } + + private subscribeToTokenChanges(): void { + // Subscribe to all fontSize and lineHeight tokens + const positions = [ + typeRampScale.minus2, typeRampScale.minus1, typeRampScale.base, + typeRampScale.plus1, typeRampScale.plus2, typeRampScale.plus3, + typeRampScale.plus4, typeRampScale.plus5, typeRampScale.plus6 + ]; + + positions.forEach(position => { + position.fontSize.subscribe(this); + position.lineHeight.subscribe(this); + }); + + // Also subscribe to multiplier and ratio changes + typeRampScale.multiplier.subscribe(this); + typeRampScale.lineHeightRatio.subscribe(this); + typeRampScale.lineHeightSnap.subscribe(this); + } + + handleChange(token: DesignToken, record: DesignTokenChangeRecord): void { + if (!this.updateScheduled) { + this.updateScheduled = true; + requestAnimationFrame(() => { + this.updateComputedValues(); + this.updateScheduled = false; + }); + } + } + + private updateComputedValues(): void { + const positions = ["minus2", "minus1", "base", "plus1", "plus2", "plus3", "plus4", "plus5", "plus6"]; + + positions.forEach(position => { + const element = this.shadowRoot?.querySelector(`p.${position}`) as HTMLElement; + if (element) { + const computedStyle = getComputedStyle(element); + (this as any)[position] = { + fontSize: computedStyle.fontSize, + lineHeight: computedStyle.lineHeight + }; + } + }); + } +} diff --git a/packages/adaptive-ui/docs/api-report.md b/packages/adaptive-ui/docs/api-report.md index ac28fcc8..90532317 100644 --- a/packages/adaptive-ui/docs/api-report.md +++ b/packages/adaptive-ui/docs/api-report.md @@ -11,6 +11,7 @@ import { CSSDesignToken } from '@microsoft/fast-foundation'; import { CSSDirective } from '@microsoft/fast-element'; import { DesignToken } from '@microsoft/fast-foundation'; import { DesignTokenResolver } from '@microsoft/fast-foundation'; +import { DesignTokenValue } from '@microsoft/fast-foundation/design-token-core.js'; import { ElementStyles } from '@microsoft/fast-element'; import { Rgb } from 'culori/fn'; import { TypedCSSDesignToken as TypedCSSDesignToken_2 } from '../adaptive-design-tokens.js'; @@ -864,6 +865,38 @@ export class TypedDesignToken extends DesignToken implements DesignTokenMe export interface TypedDesignToken extends DesignTokenMetadata { } +// @public +export class TypeRampPosition { + constructor(baseName: string, position: string, fontSize: DesignTokenValue, lineHeight: DesignTokenValue); + readonly fontSize: TypedCSSDesignToken; + readonly fontVariations: TypedCSSDesignToken; + readonly lineHeight: TypedCSSDesignToken; +} + +// @public +export class TypeRampTokenGroup implements TokenGroup { + constructor(name: string, minus2FontSize: DesignTokenValue, minus2LineHeight: DesignTokenValue, minus1FontSize: DesignTokenValue, minus1LineHeight: DesignTokenValue, baseFontSize: DesignTokenValue, baseLineHeight: DesignTokenValue, plus1FontSize: DesignTokenValue, plus1LineHeight: DesignTokenValue, plus2FontSize: DesignTokenValue, plus2LineHeight: DesignTokenValue, plus3FontSize: DesignTokenValue, plus3LineHeight: DesignTokenValue, plus4FontSize: DesignTokenValue, plus4LineHeight: DesignTokenValue, plus5FontSize: DesignTokenValue, plus5LineHeight: DesignTokenValue, plus6FontSize: DesignTokenValue, plus6LineHeight: DesignTokenValue); + readonly base: TypeRampPosition; + readonly minus1: TypeRampPosition; + readonly minus2: TypeRampPosition; + // (undocumented) + readonly name: string; + readonly plus1: TypeRampPosition; + readonly plus2: TypeRampPosition; + readonly plus3: TypeRampPosition; + readonly plus4: TypeRampPosition; + readonly plus5: TypeRampPosition; + readonly plus6: TypeRampPosition; +} + +// @public +export class TypeScaleTokenGroup extends TypeRampTokenGroup { + constructor(name: string, baseSize: string, multiplier: number, lineHeightRatio?: number, lineHeightSnap?: string); + readonly lineHeightRatio: TypedDesignToken; + readonly lineHeightSnap: TypedCSSDesignToken; + readonly multiplier: TypedDesignToken; +} + // @internal export const _white: Color; diff --git a/packages/adaptive-ui/src/core/index.ts b/packages/adaptive-ui/src/core/index.ts index 086f8fa0..b794b8af 100644 --- a/packages/adaptive-ui/src/core/index.ts +++ b/packages/adaptive-ui/src/core/index.ts @@ -5,6 +5,7 @@ export * from "./modules/index.js"; export * from "./adaptive-design-tokens.js"; export * from "./recipes.js"; export * from "./shadow/index.js"; +export * from "./typography/index.js"; export * from "./token-helpers-color.js"; export * from "./token-helpers.js"; export * from "./types.js"; diff --git a/packages/adaptive-ui/src/core/typography/index.ts b/packages/adaptive-ui/src/core/typography/index.ts new file mode 100644 index 00000000..52f257e0 --- /dev/null +++ b/packages/adaptive-ui/src/core/typography/index.ts @@ -0,0 +1 @@ +export * from "./type-ramp.js"; diff --git a/packages/adaptive-ui/src/core/typography/type-ramp.ts b/packages/adaptive-ui/src/core/typography/type-ramp.ts index f3a1019a..40c5b109 100644 --- a/packages/adaptive-ui/src/core/typography/type-ramp.ts +++ b/packages/adaptive-ui/src/core/typography/type-ramp.ts @@ -1,10 +1,13 @@ import type { DesignToken, DesignTokenResolver } from "@microsoft/fast-foundation"; -import type { TypedCSSDesignToken } from "../adaptive-design-tokens.js"; +import { DesignTokenValue } from "@microsoft/fast-foundation/design-token-core.js"; +import { type TypedCSSDesignToken, type TypedDesignToken } from "../adaptive-design-tokens.js"; import { TokenGroup } from "../types.js"; import { + createTokenDimension, createTokenFontSize, createTokenFontVariations, - createTokenLineHeight + createTokenLineHeight, + createTokenNumber } from "../token-helpers.js"; // TODO: This should be a recipe. Reevaluate as design tokens update. @@ -52,8 +55,8 @@ export class TypeRampPosition { constructor( baseName: string, position: string, - fontSize: string, - lineHeight: string, + fontSize: DesignTokenValue, + lineHeight: DesignTokenValue, ) { this.fontSize = createTokenFontSize(`${baseName}.fontSize.${position}`).withDefault(fontSize); this.lineHeight = createTokenLineHeight(`${baseName}.lineHeight.${position}`).withDefault(lineHeight); @@ -155,24 +158,24 @@ export class TypeRampTokenGroup implements TokenGroup { */ constructor( public readonly name: string, - minus2FontSize: string, - minus2LineHeight: string, - minus1FontSize: string, - minus1LineHeight: string, - baseFontSize: string, - baseLineHeight: string, - plus1FontSize: string, - plus1LineHeight: string, - plus2FontSize: string, - plus2LineHeight: string, - plus3FontSize: string, - plus3LineHeight: string, - plus4FontSize: string, - plus4LineHeight: string, - plus5FontSize: string, - plus5LineHeight: string, - plus6FontSize: string, - plus6LineHeight: string + minus2FontSize: DesignTokenValue, + minus2LineHeight: DesignTokenValue, + minus1FontSize: DesignTokenValue, + minus1LineHeight: DesignTokenValue, + baseFontSize: DesignTokenValue, + baseLineHeight: DesignTokenValue, + plus1FontSize: DesignTokenValue, + plus1LineHeight: DesignTokenValue, + plus2FontSize: DesignTokenValue, + plus2LineHeight: DesignTokenValue, + plus3FontSize: DesignTokenValue, + plus3LineHeight: DesignTokenValue, + plus4FontSize: DesignTokenValue, + plus4LineHeight: DesignTokenValue, + plus5FontSize: DesignTokenValue, + plus5LineHeight: DesignTokenValue, + plus6FontSize: DesignTokenValue, + plus6LineHeight: DesignTokenValue ) { this.minus2 = new TypeRampPosition(name, "minus2", minus2FontSize, minus2LineHeight); this.minus1 = new TypeRampPosition(name, "minus1", minus1FontSize, minus1LineHeight); @@ -184,4 +187,165 @@ export class TypeRampTokenGroup implements TokenGroup { this.plus5 = new TypeRampPosition(name, "plus5", plus5FontSize, plus5LineHeight); this.plus6 = new TypeRampPosition(name, "plus6", plus6FontSize, plus6LineHeight); } + +} + +/** + * A type ramp generated from a base size and multiplier (type scale). + * + * In this model, the base.fontSize is the primary token that should be edited, + * and all other positions are derived from it using calc() expressions with the multiplier. + * + * @public + */ +export class TypeScaleTokenGroup extends TypeRampTokenGroup { + /** + * The multiplier token for the type scale. + * + * @public + */ + public readonly multiplier: TypedDesignToken; + + /** + * The line height ratio token for the type scale. + * + * @public + */ + public readonly lineHeightRatio: TypedDesignToken; + + /** + * The line height snap token for rounding line heights. + * + * @public + */ + public readonly lineHeightSnap: TypedCSSDesignToken; + + /** + * Creates a new type scale token group. + * + * @param name - The base name of the token group (e.g., "typography.ramp.scale"). + * @param baseSize - The base font size as a string value (e.g., "16px"). + * @param multiplier - The multiplier for the type scale (e.g., 1.25). + * @param lineHeightRatio - The line height ratio (default: 1.4). + * @param lineHeightSnap - The line height snap value for rounding line heights (default: "2px"). + */ + constructor( + name: string, + baseSize: string, + multiplier: number, + lineHeightRatio: number = 1.4, + lineHeightSnap: string = "2px" + ) { + // Create the tokens + const multiplierToken = createTokenNumber(`${name}.multiplier`).withDefault(multiplier); + const lineHeightRatioToken = createTokenNumber(`${name}.lineHeightRatio`).withDefault(lineHeightRatio); + const lineHeightSnapToken = createTokenDimension(`${name}.lineHeightSnap`).withDefault(lineHeightSnap); + + // Call parent constructor with placeholder values + // We'll update the tokens with calculated values after all position tokens are created + super( + name, + "0px", // minus2 fontSize placeholder + "0px", // minus2 lineHeight placeholder + "0px", // minus1 fontSize placeholder + "0px", // minus1 lineHeight placeholder + baseSize, // base fontSize (the only concrete value) + "0px", // base lineHeight placeholder + "0px", // plus1 fontSize placeholder + "0px", // plus1 lineHeight placeholder + "0px", // plus2 fontSize placeholder + "0px", // plus2 lineHeight placeholder + "0px", // plus3 fontSize placeholder + "0px", // plus3 lineHeight placeholder + "0px", // plus4 fontSize placeholder + "0px", // plus4 lineHeight placeholder + "0px", // plus5 fontSize placeholder + "0px", // plus5 lineHeight placeholder + "0px", // plus6 fontSize placeholder + "0px" // plus6 lineHeight placeholder + ); + + // Set the tokens + this.multiplier = multiplierToken; + this.lineHeightRatio = lineHeightRatioToken; + this.lineHeightSnap = lineHeightSnapToken; + + // Now that all position tokens exist, update them with calculated values + this.minus2.fontSize.withDefault( + (resolve: DesignTokenResolver) => + `calc(${resolve(this.base.fontSize)} / pow(${resolve(multiplierToken)}, 2))` + ); + this.minus2.lineHeight.withDefault( + (resolve: DesignTokenResolver) => + `round(${resolve(this.minus2.fontSize)} * ${resolve(lineHeightRatioToken)}, ${resolve(lineHeightSnapToken)})` + ); + + this.minus1.fontSize.withDefault( + (resolve: DesignTokenResolver) => + `calc(${resolve(this.base.fontSize)} / ${resolve(multiplierToken)})` + ); + this.minus1.lineHeight.withDefault( + (resolve: DesignTokenResolver) => + `round(${resolve(this.minus1.fontSize)} * ${resolve(lineHeightRatioToken)}, ${resolve(lineHeightSnapToken)})` + ); + + this.base.lineHeight.withDefault( + (resolve: DesignTokenResolver) => + `round(${resolve(this.base.fontSize)} * ${resolve(lineHeightRatioToken)}, ${resolve(lineHeightSnapToken)})` + ); + + this.plus1.fontSize.withDefault( + (resolve: DesignTokenResolver) => + `calc(${resolve(this.base.fontSize)} * ${resolve(multiplierToken)})` + ); + this.plus1.lineHeight.withDefault( + (resolve: DesignTokenResolver) => + `round(${resolve(this.plus1.fontSize)} * ${resolve(lineHeightRatioToken)}, ${resolve(lineHeightSnapToken)})` + ); + + this.plus2.fontSize.withDefault( + (resolve: DesignTokenResolver) => + `calc(${resolve(this.base.fontSize)} * pow(${resolve(multiplierToken)}, 2))` + ); + this.plus2.lineHeight.withDefault( + (resolve: DesignTokenResolver) => + `round(${resolve(this.plus2.fontSize)} * ${resolve(lineHeightRatioToken)}, ${resolve(lineHeightSnapToken)})` + ); + + this.plus3.fontSize.withDefault( + (resolve: DesignTokenResolver) => + `calc(${resolve(this.base.fontSize)} * pow(${resolve(multiplierToken)}, 3))` + ); + this.plus3.lineHeight.withDefault( + (resolve: DesignTokenResolver) => + `round(${resolve(this.plus3.fontSize)} * ${resolve(lineHeightRatioToken)}, ${resolve(lineHeightSnapToken)})` + ); + + this.plus4.fontSize.withDefault( + (resolve: DesignTokenResolver) => + `calc(${resolve(this.base.fontSize)} * pow(${resolve(multiplierToken)}, 4))` + ); + this.plus4.lineHeight.withDefault( + (resolve: DesignTokenResolver) => + `round(${resolve(this.plus4.fontSize)} * ${resolve(lineHeightRatioToken)}, ${resolve(lineHeightSnapToken)})` + ); + + this.plus5.fontSize.withDefault( + (resolve: DesignTokenResolver) => + `calc(${resolve(this.base.fontSize)} * pow(${resolve(multiplierToken)}, 5))` + ); + this.plus5.lineHeight.withDefault( + (resolve: DesignTokenResolver) => + `round(${resolve(this.plus5.fontSize)} * ${resolve(lineHeightRatioToken)}, ${resolve(lineHeightSnapToken)})` + ); + + this.plus6.fontSize.withDefault( + (resolve: DesignTokenResolver) => + `calc(${resolve(this.base.fontSize)} * pow(${resolve(multiplierToken)}, 6))` + ); + this.plus6.lineHeight.withDefault( + (resolve: DesignTokenResolver) => + `round(${resolve(this.plus6.fontSize)} * ${resolve(lineHeightRatioToken)}, ${resolve(lineHeightSnapToken)})` + ); + } } diff --git a/packages/adaptive-ui/src/reference/type.ts b/packages/adaptive-ui/src/reference/type.ts index 597686d7..434481b9 100644 --- a/packages/adaptive-ui/src/reference/type.ts +++ b/packages/adaptive-ui/src/reference/type.ts @@ -72,85 +72,112 @@ export const typeRampDefault = new TypeRampTokenGroup( "52px" ); +/** + * Example: Create a type scale from a base size and multiplier. + * A type scale generates all positions using a consistent multiplier and line height ratio. + * The base.fontSize is the primary editable token, and all other positions + * derive from it using calc() expressions with the multiplier. + * + * @example + * ```ts + * import { TypeScaleTokenGroup } from "../core/typography/type-ramp.js"; + * + * // Create a type scale with 1.25 ratio + * export const typeRampScale = new TypeScaleTokenGroup( + * "typography.ramp.scale", + * "16px", // base size + * 1.25, // multiplier (each step is 1.25x the previous) + * 1.4 // line height ratio (optional, defaults to 1.4) + * ); + * + * // Access the scale: + * // typeRampScale.base.fontSize - the primary editable base font size token + * // typeRampScale.multiplier - the multiplier token (also editable) + * // typeRampScale.plus1.fontSize - derived: "calc(base * multiplier)" + * // typeRampScale.plus2.fontSize - derived: "calc(base * pow(multiplier, 2))" + * // typeRampScale.minus1.fontSize - derived: "calc(base / multiplier)" + * ``` + */ + // Export individual tokens for backward compatibility -/** @public */ +/** @deprecated Use `typeRampDefault.minus2.fontSize` directly. @public */ export const typeRampMinus2FontSize = typeRampDefault.minus2.fontSize; -/** @public */ +/** @deprecated Use `typeRampDefault.minus2.lineHeight` directly. @public */ export const typeRampMinus2LineHeight = typeRampDefault.minus2.lineHeight; -/** @public */ +/** @deprecated Use `typeRampDefault.minus2.fontVariations` directly. @public */ export const typeRampMinus2FontVariations = typeRampDefault.minus2.fontVariations; -/** @public */ +/** @deprecated Use `typeRampDefault.minus1.fontSize` directly. @public */ export const typeRampMinus1FontSize = typeRampDefault.minus1.fontSize; -/** @public */ +/** @deprecated Use `typeRampDefault.minus1.lineHeight` directly. @public */ export const typeRampMinus1LineHeight = typeRampDefault.minus1.lineHeight; -/** @public */ +/** @deprecated Use `typeRampDefault.minus1.fontVariations` directly. @public */ export const typeRampMinus1FontVariations = typeRampDefault.minus1.fontVariations; -/** @public */ +/** @deprecated Use `typeRampDefault.base.fontSize` directly. @public */ export const typeRampBaseFontSize = typeRampDefault.base.fontSize; -/** @public */ +/** @deprecated Use `typeRampDefault.base.lineHeight` directly. @public */ export const typeRampBaseLineHeight = typeRampDefault.base.lineHeight; -/** @public */ +/** @deprecated Use `typeRampDefault.base.fontVariations` directly. @public */ export const typeRampBaseFontVariations = typeRampDefault.base.fontVariations; -/** @public */ +/** @deprecated Use `typeRampDefault.plus1.fontSize` directly. @public */ export const typeRampPlus1FontSize = typeRampDefault.plus1.fontSize; -/** @public */ +/** @deprecated Use `typeRampDefault.plus1.lineHeight` directly. @public */ export const typeRampPlus1LineHeight = typeRampDefault.plus1.lineHeight; -/** @public */ +/** @deprecated Use `typeRampDefault.plus1.fontVariations` directly. @public */ export const typeRampPlus1FontVariations = typeRampDefault.plus1.fontVariations; -/** @public */ +/** @deprecated Use `typeRampDefault.plus2.fontSize` directly. @public */ export const typeRampPlus2FontSize = typeRampDefault.plus2.fontSize; -/** @public */ +/** @deprecated Use `typeRampDefault.plus2.lineHeight` directly. @public */ export const typeRampPlus2LineHeight = typeRampDefault.plus2.lineHeight; -/** @public */ +/** @deprecated Use `typeRampDefault.plus2.fontVariations` directly. @public */ export const typeRampPlus2FontVariations = typeRampDefault.plus2.fontVariations; -/** @public */ +/** @deprecated Use `typeRampDefault.plus3.fontSize` directly. @public */ export const typeRampPlus3FontSize = typeRampDefault.plus3.fontSize; -/** @public */ +/** @deprecated Use `typeRampDefault.plus3.lineHeight` directly. @public */ export const typeRampPlus3LineHeight = typeRampDefault.plus3.lineHeight; -/** @public */ +/** @deprecated Use `typeRampDefault.plus3.fontVariations` directly. @public */ export const typeRampPlus3FontVariations = typeRampDefault.plus3.fontVariations; -/** @public */ +/** @deprecated Use `typeRampDefault.plus4.fontSize` directly. @public */ export const typeRampPlus4FontSize = typeRampDefault.plus4.fontSize; -/** @public */ +/** @deprecated Use `typeRampDefault.plus4.lineHeight` directly. @public */ export const typeRampPlus4LineHeight = typeRampDefault.plus4.lineHeight; -/** @public */ +/** @deprecated Use `typeRampDefault.plus4.fontVariations` directly. @public */ export const typeRampPlus4FontVariations = typeRampDefault.plus4.fontVariations; -/** @public */ +/** @deprecated Use `typeRampDefault.plus5.fontSize` directly. @public */ export const typeRampPlus5FontSize = typeRampDefault.plus5.fontSize; -/** @public */ +/** @deprecated Use `typeRampDefault.plus5.lineHeight` directly. @public */ export const typeRampPlus5LineHeight = typeRampDefault.plus5.lineHeight; -/** @public */ +/** @deprecated Use `typeRampDefault.plus5.fontVariations` directly. @public */ export const typeRampPlus5FontVariations = typeRampDefault.plus5.fontVariations; -/** @public */ +/** @deprecated Use `typeRampDefault.plus6.fontSize` directly. @public */ export const typeRampPlus6FontSize = typeRampDefault.plus6.fontSize; -/** @public */ +/** @deprecated Use `typeRampDefault.plus6.lineHeight` directly. @public */ export const typeRampPlus6LineHeight = typeRampDefault.plus6.lineHeight; -/** @public */ -export const typeRampPlus6FontVariations = typeRampDefault.plus6.fontVariations; \ No newline at end of file +/** @deprecated Use `typeRampDefault.plus6.fontVariations` directly. @public */ +export const typeRampPlus6FontVariations = typeRampDefault.plus6.fontVariations;