diff --git a/change/@fluentui-web-components-91e2200b-d44a-48fe-9075-c23eced8be5f.json b/change/@fluentui-web-components-91e2200b-d44a-48fe-9075-c23eced8be5f.json new file mode 100644 index 0000000000000..a2df71a8e4766 --- /dev/null +++ b/change/@fluentui-web-components-91e2200b-d44a-48fe-9075-c23eced8be5f.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "add color-vNext folder with recipes and update specs", + "packageName": "@fluentui/web-components", + "email": "chhol@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/web-components/docs/api-report.md b/packages/web-components/docs/api-report.md index 10872a23915b3..8280e4d2b30c5 100644 --- a/packages/web-components/docs/api-report.md +++ b/packages/web-components/docs/api-report.md @@ -6,16 +6,16 @@ import { Accordion } from '@microsoft/fast-foundation'; import { AccordionItem } from '@microsoft/fast-foundation'; -import { Anchor } from '@microsoft/fast-foundation'; +import { Anchor as Anchor_2 } from '@microsoft/fast-foundation'; import { AnchoredRegion } from '@microsoft/fast-foundation'; -import { Badge } from '@microsoft/fast-foundation'; +import { Badge as Badge_2 } from '@microsoft/fast-foundation'; import { BaseProgress } from '@microsoft/fast-foundation'; import { Breadcrumb } from '@microsoft/fast-foundation'; import { BreadcrumbItem } from '@microsoft/fast-foundation'; -import { Button } from '@microsoft/fast-foundation'; +import { Button as Button_2 } from '@microsoft/fast-foundation'; import { Checkbox } from '@microsoft/fast-foundation'; import { ColorRGBA64 } from '@microsoft/fast-colors'; -import { Combobox } from '@microsoft/fast-foundation'; +import { Combobox as Combobox_2 } from '@microsoft/fast-foundation'; import { CSSCustomPropertyBehavior } from '@microsoft/fast-foundation'; import { DataGrid } from '@microsoft/fast-foundation'; import { DataGridCell } from '@microsoft/fast-foundation'; @@ -26,15 +26,15 @@ import { Direction } from '@microsoft/fast-web-utilities'; import { Divider } from '@microsoft/fast-foundation'; import { ElementStyles } from '@microsoft/fast-element'; import { Flipper } from '@microsoft/fast-foundation'; -import { HorizontalScroll } from '@microsoft/fast-foundation'; +import { HorizontalScroll as HorizontalScroll_2 } from '@microsoft/fast-foundation'; import { Listbox } from '@microsoft/fast-foundation'; import { ListboxOption } from '@microsoft/fast-foundation'; import { Menu } from '@microsoft/fast-foundation'; import { MenuItem } from '@microsoft/fast-foundation'; -import { NumberField } from '@microsoft/fast-foundation'; +import { NumberField as NumberField_2 } from '@microsoft/fast-foundation'; import { Radio } from '@microsoft/fast-foundation'; import { RadioGroup } from '@microsoft/fast-foundation'; -import { Select } from '@microsoft/fast-foundation'; +import { Select as Select_2 } from '@microsoft/fast-foundation'; import { Skeleton } from '@microsoft/fast-foundation'; import { Slider } from '@microsoft/fast-foundation'; import { SliderLabel } from '@microsoft/fast-foundation'; @@ -42,8 +42,8 @@ import { Switch } from '@microsoft/fast-foundation'; import { Tab } from '@microsoft/fast-foundation'; import { TabPanel } from '@microsoft/fast-foundation'; import { Tabs } from '@microsoft/fast-foundation'; -import { TextArea } from '@microsoft/fast-foundation'; -import { TextField } from '@microsoft/fast-foundation'; +import { TextArea as TextArea_2 } from '@microsoft/fast-foundation'; +import { TextField as TextField_2 } from '@microsoft/fast-foundation'; import { Tooltip } from '@microsoft/fast-foundation'; import { TreeItem } from '@microsoft/fast-foundation'; import { TreeView } from '@microsoft/fast-foundation'; @@ -214,30 +214,51 @@ export const accentForegroundRest: SwatchRecipe; export const accentForegroundRestBehavior: CSSCustomPropertyBehavior; // @public -export const AccordionItemStyles: import("@microsoft/fast-element").ElementStyles; +export const accordionItemStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // @public -export const AccordionStyles: import("@microsoft/fast-element").ElementStyles; +export const accordionStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // Warning: (ae-internal-missing-underscore) The name "ambientShadow" should be prefixed with an underscore because the declaration is marked as @internal // // @internal export const ambientShadow = "0 0 calc((var(--elevation) * 0.225px) + 2px) rgba(0, 0, 0, calc(.11 * (2 - var(--background-luminance, 1))))"; +// Warning: (ae-internal-missing-underscore) The name "Anchor" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class Anchor extends Anchor_2 { + // @public + appearance: AnchorAppearance; + // (undocumented) + appearanceChanged(oldValue: AnchorAppearance, newValue: AnchorAppearance): void; + // (undocumented) + connectedCallback(): void; + defaultSlottedContentChanged(): void; +} + // @public export type AnchorAppearance = ButtonAppearance | 'hypertext'; // @public -export const AnchoredRegionStyles: import("@microsoft/fast-element").ElementStyles; +export const anchoredRegionStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // @public -export const AnchorStyles: import("@microsoft/fast-element").ElementStyles; +export const anchorStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; + +// Warning: (ae-internal-missing-underscore) The name "Badge" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class Badge extends Badge_2 { + // (undocumented) + appearance: BadgeAppearance; + } // @public export type BadgeAppearance = 'accent' | 'lightweight' | 'neutral' | string; // @public -export const BadgeStyles: import("@microsoft/fast-element").ElementStyles; +export const badgeStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // Warning: (ae-internal-missing-underscore) The name "BaseButtonStyles" should be prefixed with an underscore because the declaration is marked as @internal // @@ -245,40 +266,65 @@ export const BadgeStyles: import("@microsoft/fast-element").ElementStyles; export const BaseButtonStyles: ElementStyles; // @public -export const BreadcrumbItemStyles: import("@microsoft/fast-element").ElementStyles; +export const breadcrumbItemStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // @public -export const BreadcrumbStyles: import("@microsoft/fast-element").ElementStyles; +export const breadcrumbStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; + +// Warning: (ae-internal-missing-underscore) The name "Button" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class Button extends Button_2 { + // @public + appearance: ButtonAppearance; + // (undocumented) + appearanceChanged(oldValue: ButtonAppearance, newValue: ButtonAppearance): void; + // (undocumented) + connectedCallback(): void; + defaultSlottedContentChanged(): void; +} // @public export type ButtonAppearance = 'accent' | 'lightweight' | 'neutral' | 'outline' | 'stealth'; // @public -export const ButtonStyles: import("@microsoft/fast-element").ElementStyles; +export const buttonStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // @public export const CardStyles: import("@microsoft/fast-element").ElementStyles; // @public -export const CheckboxStyles: import("@microsoft/fast-element").ElementStyles; +export const checkboxStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; + +// Warning: (ae-internal-missing-underscore) The name "Combobox" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class Combobox extends Combobox_2 { + // @public + appearance: ComboboxAppearance; + // (undocumented) + appearanceChanged(oldValue: ComboboxAppearance, newValue: ComboboxAppearance): void; + // (undocumented) + connectedCallback(): void; +} // @public export type ComboboxAppearance = SelectAppearance; // @public -export const ComboboxStyles: import("@microsoft/fast-element").ElementStyles; +export const comboboxStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // @public export function createColorPalette(baseColor: any): string[]; // @public -export const DataGridCellStyles: import("@microsoft/fast-element").ElementStyles; +export const dataGridCellStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // @public -export const DataGridRowStyles: import("@microsoft/fast-element").ElementStyles; +export const dataGridRowStyles: (context: any, defintion: any) => import("@microsoft/fast-element").ElementStyles; // @public -export const DataGridStyles: import("@microsoft/fast-element").ElementStyles; +export const dataGridStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // @public export interface DesignSystem { @@ -410,7 +456,7 @@ export interface DesignSystem { export const DesignSystemDefaults: DesignSystem; // @public -export const DialogStyles: import("@microsoft/fast-element").ElementStyles; +export const dialogStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // Warning: (ae-internal-missing-underscore) The name "directionalShadow" should be prefixed with an underscore because the declaration is marked as @internal // @@ -418,7 +464,7 @@ export const DialogStyles: import("@microsoft/fast-element").ElementStyles; export const directionalShadow = "0 calc(var(--elevation) * 0.4px) calc((var(--elevation) * 0.9px)) rgba(0, 0, 0, calc(.13 * (2 - var(--background-luminance, 1))))"; // @public -export const DividerStyles: import("@microsoft/fast-element").ElementStyles; +export const dividerStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // @public export const elevation: string; @@ -429,55 +475,119 @@ export const elevation: string; export const FillStateStyles: ElementStyles; // @public -export const FlipperStyles: import("@microsoft/fast-element").ElementStyles; - -// @public -export class FluentAccordion extends Accordion { -} - -// @public -export class FluentAccordionItem extends AccordionItem { -} - -// @public -export class FluentAnchor extends Anchor { - appearance: AnchorAppearance; - // (undocumented) - appearanceChanged(oldValue: AnchorAppearance, newValue: AnchorAppearance): void; - // @internal (undocumented) - connectedCallback(): void; - // @internal - defaultSlottedContentChanged(): void; -} +export const flipperStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; + +// @public +export const fluentAccordion: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof Accordion>; + +// @public +export const fluentAccordionItem: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof AccordionItem>; + +// Warning: (ae-incompatible-release-tags) The symbol "fluentAnchor" is marked as @public, but its signature references "Anchor" which is marked as @internal +// +// @public +export const fluentAnchor: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; + shadowOptions: { + delegatesFocus: true; + }; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; + shadowOptions: { + delegatesFocus: true; + }; +}, typeof Anchor>; // @beta -export class FluentAnchoredRegion extends AnchoredRegion { -} - -// @public -export class FluentBadge extends Badge { - // (undocumented) - appearance: BadgeAppearance; - } - -// @public -export class FluentBreadcrumb extends Breadcrumb { -} - -// @public -export class FluentBreadcrumbItem extends BreadcrumbItem { -} - -// @public -export class FluentButton extends Button { - appearance: ButtonAppearance; - // (undocumented) - appearanceChanged(oldValue: ButtonAppearance, newValue: ButtonAppearance): void; - // @internal (undocumented) - connectedCallback(): void; - // @internal - defaultSlottedContentChanged(): void; -} +export const fluentAnchoredRegion: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof AnchoredRegion>; + +// Warning: (ae-incompatible-release-tags) The symbol "fluentBadge" is marked as @public, but its signature references "Badge" which is marked as @internal +// +// @public +export const fluentBadge: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof Badge>; + +// @public +export const fluentBreadcrumb: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof Breadcrumb>; + +// @public +export const fluentBreadcrumbItem: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; + shadowOptions: { + delegatesFocus: true; + }; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; + shadowOptions: { + delegatesFocus: true; + }; +}, typeof BreadcrumbItem>; + +// Warning: (ae-incompatible-release-tags) The symbol "fluentButton" is marked as @public, but its signature references "Button" which is marked as @internal +// +// @public +export const fluentButton: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; + shadowOptions: { + delegatesFocus: true; + }; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; + shadowOptions: { + delegatesFocus: true; + }; +}, typeof Button>; // @public export class FluentCard extends FluentDesignSystemProvider { @@ -489,29 +599,61 @@ export class FluentCard extends FluentDesignSystemProvider { } // @public -export class FluentCheckbox extends Checkbox { -} - -// @public -export class FluentCombobox extends Combobox { - appearance: ComboboxAppearance; - // @internal (undocumented) - appearanceChanged(oldValue: ComboboxAppearance, newValue: ComboboxAppearance): void; - // @internal (undocumented) - connectedCallback(): void; -} - -// @public -export class FluentDataGrid extends DataGrid { -} - -// @public -export class FluentDataGridCell extends DataGridCell { -} - -// @public -export class FluentDataGridRow extends DataGridRow { -} +export const fluentCheckbox: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof Checkbox>; + +// Warning: (ae-incompatible-release-tags) The symbol "fluentCombobox" is marked as @public, but its signature references "Combobox" which is marked as @internal +// +// @public +export const fluentCombobox: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof Combobox>; + +// @public +export const fluentDataGrid: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof DataGrid>; + +// @public +export const fluentDataGridCell: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof DataGridCell>; + +// @public +export const fluentDataGridRow: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, defintion: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, defintion: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof DataGridRow>; // @public export type FluentDesignSystem = Omit; @@ -673,135 +815,330 @@ export class FluentDesignSystemProvider extends DesignSystemProvider implements } // @public -export class FluentDialog extends Dialog { -} - -// @public -export class FluentDivider extends Divider { -} - -// @public -export class FluentFlipper extends Flipper { -} - -// @public -export class FluentHorizontalScroll extends HorizontalScroll { - // (undocumented) - connectedCallback(): void; -} - -// @public -export class FluentListbox extends Listbox { -} - -// @public -export class FluentMenu extends Menu { -} - -// @public -export class FluentMenuItem extends MenuItem { -} - -// @public -export class FluentNumberField extends NumberField { - appearance: NumberFieldAppearance; - // @internal (undocumented) - connectedCallback(): void; -} - -// @public -export class FluentOption extends ListboxOption { -} - -// @public -export class FluentProgress extends BaseProgress { -} - -// @public -export class FluentProgressRing extends BaseProgress { -} - -// @public -export class FluentRadio extends Radio { -} - -// @public -export class FluentRadioGroup extends RadioGroup { -} - -// @public -export class FluentSelect extends Select { - appearance: SelectAppearance; - // @internal (undocumented) - appearanceChanged(oldValue: SelectAppearance, newValue: SelectAppearance): void; - // @internal (undocumented) - connectedCallback(): void; -} - -// @public -export class FluentSkeleton extends Skeleton { -} - -// @public -export class FluentSlider extends Slider { -} - -// @public -export class FluentSliderLabel extends SliderLabel { -} - -// @public -export class FluentSwitch extends Switch { -} - -// @public -export class FluentTab extends Tab { -} +export const fluentDialog: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof Dialog>; + +// @public +export const fluentDivider: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof Divider>; + +// @public +export const fluentFlipper: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof Flipper>; + +// Warning: (ae-incompatible-release-tags) The symbol "fluentHorizontalScroll" is marked as @public, but its signature references "HorizontalScroll" which is marked as @internal +// +// @public +export const fluentHorizontalScroll: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof HorizontalScroll>; + +// @public +export const fluentListbox: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof Listbox>; + +// @public +export const fluentMenu: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof Menu>; + +// @public +export const fluentMenuItem: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof MenuItem>; + +// Warning: (ae-incompatible-release-tags) The symbol "fluentNumberField" is marked as @public, but its signature references "NumberField" which is marked as @internal +// +// @public +export const fluentNumberField: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + shadowOptions: { + delegatesFocus: true; + }; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + shadowOptions: { + delegatesFocus: true; + }; +}, typeof NumberField>; + +// @public +export const fluentOption: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof ListboxOption>; + +// @public +export const fluentProgress: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, defintion: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, defintion: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof BaseProgress>; + +// @public +export const fluentProgressRing: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof BaseProgress>; + +// @public +export const fluentRadio: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof Radio>; + +// @public +export const fluentRadioGroup: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof RadioGroup>; + +// Warning: (ae-incompatible-release-tags) The symbol "fluentSelect" is marked as @public, but its signature references "Select" which is marked as @internal +// +// @public +export const fluentSelect: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof Select>; + +// @public +export const fluentSkeleton: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof Skeleton>; + +// @public +export const fluentSlider: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, defintion: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, defintion: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof Slider>; + +// @public +export const fluentSliderLabel: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof SliderLabel>; + +// @public +export const fluentSwitch: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, defintiion: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, defintiion: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof Switch>; + +// @public +export const fluentTab: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof Tab>; + +// @public +export const fluentTabPanel: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof TabPanel>; + +// @public +export const fluentTabs: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof Tabs>; + +// Warning: (ae-incompatible-release-tags) The symbol "fluentTextArea" is marked as @public, but its signature references "TextArea" which is marked as @internal +// +// @public +export const fluentTextArea: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; + shadowOptions: { + delegatesFocus: true; + }; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; + shadowOptions: { + delegatesFocus: true; + }; +}, typeof TextArea>; + +// Warning: (ae-incompatible-release-tags) The symbol "fluentTextField" is marked as @public, but its signature references "TextField" which is marked as @internal +// +// @public +export const fluentTextField: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; + shadowOptions: { + delegatesFocus: true; + }; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; + shadowOptions: { + delegatesFocus: true; + }; +}, typeof TextField>; + +// @public +export const fluentTooltip: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof Tooltip>; + +// @public +export const fluentTreeItem: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof TreeItem>; + +// @public +export const fluentTreeView: (overrideDefinition?: import("@microsoft/fast-foundation").OverrideFoundationElementDefinition<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}> | undefined) => import("@microsoft/fast-foundation").FoundationElementRegistry<{ + baseName: string; + template: (context: any, definition: any) => import("@microsoft/fast-element").ViewTemplate; + styles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; +}, typeof TreeView>; // @public -export class FluentTabPanel extends TabPanel { -} - -// @public -export class FluentTabs extends Tabs { -} - -// @public -export class FluentTextArea extends TextArea { - appearance: TextAreaAppearance; - // @internal (undocumented) - appearanceChanged(oldValue: TextAreaAppearance, newValue: TextAreaAppearance): void; - // @internal (undocumented) - connectedCallback(): void; -} +export const heightNumber = "(var(--base-height-multiplier) + var(--density)) * var(--design-unit)"; -// @public -export class FluentTextField extends TextField { - appearance: TextFieldAppearance; - // @internal (undocumented) - appearanceChanged(oldValue: TextFieldAppearance, newValue: TextFieldAppearance): void; - // @internal (undocumented) +// Warning: (ae-internal-missing-underscore) The name "HorizontalScroll" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal (undocumented) +export class HorizontalScroll extends HorizontalScroll_2 { + // @public (undocumented) connectedCallback(): void; } -// @public -export class FluentTooltip extends Tooltip { -} - -// @public -export class FluentTreeItem extends TreeItem { -} - -// @public -export class FluentTreeView extends TreeView { -} - -// @public -export const heightNumber = "(var(--base-height-multiplier) + var(--density)) * var(--design-unit)"; - -// @public -export const HorizontalScrollStyles: import("@microsoft/fast-element").ElementStyles; - // Warning: (ae-internal-missing-underscore) The name "HypertextStyles" should be prefixed with an underscore because the declaration is marked as @internal // // @internal (undocumented) @@ -822,13 +1159,13 @@ export function isDarkMode(designSystem: DesignSystem): boolean; export const LightweightButtonStyles: ElementStyles; // @public -export const ListboxStyles: import("@microsoft/fast-element").ElementStyles; +export const listboxStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // @public -export const MenuItemStyles: import("@microsoft/fast-element").ElementStyles; +export const menuItemStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // @public -export const MenuStyles: import("@microsoft/fast-element").ElementStyles; +export const menuStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // Warning: (ae-internal-missing-underscore) The name "neutralContrastFill" should be prefixed with an underscore because the declaration is marked as @internal // @@ -1235,14 +1572,24 @@ export const neutralOutlineRest: SwatchRecipe; // @public export const neutralOutlineRestBehavior: CSSCustomPropertyBehavior; +// Warning: (ae-internal-missing-underscore) The name "NumberField" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class NumberField extends NumberField_2 { + // @public + appearance: NumberFieldAppearance; + // (undocumented) + connectedCallback(): void; +} + // @public export type NumberFieldAppearance = 'filled' | 'outline'; // @public -export const NumberFieldStyles: import("@microsoft/fast-element").ElementStyles; +export const numberFieldStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // @public -export const OptionStyles: import("@microsoft/fast-element").ElementStyles; +export const OptionStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // Warning: (ae-internal-missing-underscore) The name "OutlineButtonStyles" should be prefixed with an underscore because the declaration is marked as @internal // @@ -1269,31 +1616,43 @@ export enum PaletteType { export function parseColorString(color: string): ColorRGBA64; // @public -export const ProgressRingStyles: import("@microsoft/fast-element").ElementStyles; +export const progressRingStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // @public -export const ProgressStyles: import("@microsoft/fast-element").ElementStyles; +export const progressStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // @public -export const RadioGroupStyles: import("@microsoft/fast-element").ElementStyles; +export const radioGroupStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // @public -export const RadioStyles: import("@microsoft/fast-element").ElementStyles; +export const RadioStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; + +// Warning: (ae-internal-missing-underscore) The name "Select" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class Select extends Select_2 { + // @public + appearance: SelectAppearance; + // (undocumented) + appearanceChanged(oldValue: SelectAppearance, newValue: SelectAppearance): void; + // (undocumented) + connectedCallback(): void; +} // @public export type SelectAppearance = 'filled' | 'outline'; // @public -export const SelectStyles: import("@microsoft/fast-element").ElementStyles; +export const selectStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // @public -export const SkeletonStyles: import("@microsoft/fast-element").ElementStyles; +export const skeletonStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // @public -export const SliderLabelStyles: import("@microsoft/fast-element").ElementStyles; +export const sliderLabelStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // @public -export const SliderStyles: import("@microsoft/fast-element").ElementStyles; +export const sliderStyles: (context: any, defintion: any) => import("@microsoft/fast-element").ElementStyles; // @public export enum StandardLuminance { @@ -1309,37 +1668,58 @@ export enum StandardLuminance { export const StealthButtonStyles: ElementStyles; // @public -export const SwitchStyles: import("@microsoft/fast-element").ElementStyles; +export const switchStyles: (context: any, defintiion: any) => import("@microsoft/fast-element").ElementStyles; // @public -export const TabPanelStyles: import("@microsoft/fast-element").ElementStyles; +export const tabPanelStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // @public -export const TabsStyles: import("@microsoft/fast-element").ElementStyles; +export const tabsStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // @public -export const TabStyles: import("@microsoft/fast-element").ElementStyles; +export const tabStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; + +// Warning: (ae-internal-missing-underscore) The name "TextArea" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class TextArea extends TextArea_2 { + // @public + appearance: TextAreaAppearance; + // (undocumented) + appearanceChanged(oldValue: TextAreaAppearance, newValue: TextAreaAppearance): void; + // (undocumented) + connectedCallback(): void; +} // @public export type TextAreaAppearance = 'filled' | 'outline'; // @public -export const TextAreaStyles: import("@microsoft/fast-element").ElementStyles; +export const textAreaStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; -// @public -export type TextFieldAppearance = 'filled' | 'outline'; +// Warning: (ae-internal-missing-underscore) The name "TextField" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal +export class TextField extends TextField_2 { + // @public + appearance: TextFieldAppearance; + // (undocumented) + appearanceChanged(oldValue: TextFieldAppearance, newValue: TextFieldAppearance): void; + // (undocumented) + connectedCallback(): void; +} // @public -export const TextFieldStyles: import("@microsoft/fast-element").ElementStyles; +export type TextFieldAppearance = 'filled' | 'outline'; // @public -export const TooltipStyles: import("@microsoft/fast-element").ElementStyles; +export const textFieldStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // @public -export const TreeItemStyles: import("@microsoft/fast-element").ElementStyles; +export const treeItemStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // @public -export const TreeViewStyles: import("@microsoft/fast-element").ElementStyles; +export const treeViewStyles: (context: any, definition: any) => import("@microsoft/fast-element").ElementStyles; // (No @packageDocumentation comment for this package) diff --git a/packages/web-components/src/color-vNext/README.md b/packages/web-components/src/color-vNext/README.md new file mode 100644 index 0000000000000..02586f1f9ca90 --- /dev/null +++ b/packages/web-components/src/color-vNext/README.md @@ -0,0 +1,32 @@ +# Fluent Color Recipes + +Color recipes are named colors who's value is algorithmically defined from a variety of inputs. `@fluentui/web-components` relies on these recipes heavily to achieve expressive theming options while maintaining color accessability targets. + +## Swatch + +A Swatch is a representation of a color that has a `relativeLuminance` value and a method to convert the swatch to a color string. It is used by recipes to determine which colors to use for UI. + +### SwatchRGB + +A concrete implementation of `Swatch`, it is a swatch with red, green, and blue 64bit color channels . + +**Example: Creating a SwatchRGB** + +```ts +import { SwatchRGB } from '@fluentui/web-components'; + +const red = SwatchRGB.create(1, 0, 0); +``` + +## Palette + +A palette is a collection `Swatch` instances, ordered by relative luminance, and provides mechanisms to safely retrieve swatches by index and by target contrast ratios. It also contains a `source` color, which is the color from which the palette is + +### PaletteRGB + +An implementation of `Palette` of `SwatchRGB` instances. + +```ts +// Create a palette from the red swatch +const palette = PaletteRGB.create(red): +``` diff --git a/packages/web-components/src/color-vNext/palette.ts b/packages/web-components/src/color-vNext/palette.ts new file mode 100644 index 0000000000000..92df02283fb38 --- /dev/null +++ b/packages/web-components/src/color-vNext/palette.ts @@ -0,0 +1,143 @@ +import { clamp, ColorRGBA64, ComponentStateColorPalette, parseColorHexRGB } from '@microsoft/fast-colors'; +import { Swatch, SwatchRGB } from './swatch'; +import { binarySearch } from './utilities/binary-search'; +import { directionByIsDark } from './utilities/direction-by-is-dark'; +import { contrast, RelativeLuminance } from './utilities/relative-luminance'; + +/** + * A collection of {@link Swatch} instances + * @public + */ +export interface Palette { + readonly source: T; + readonly swatches: ReadonlyArray; + + /** + * Returns a swatch from the palette that most closely matches + * the contrast ratio provided to a provided reference. + */ + colorContrast(reference: Swatch, contrast: number, initialIndex?: number, direction?: 1 | -1): T; + + /** + * Returns the index of the palette that most closely matches + * the relativeLuminance of the provided swatch + */ + closestIndexOf(reference: RelativeLuminance): number; + + /** + * Gets a swatch by index. Index is clamped to the limits + * of the palette so a Swatch will always be returned. + */ + get(index: number): T; +} + +export type PaletteRGB = Palette; + +export const PaletteRGB = Object.freeze({ + create(source: SwatchRGB): PaletteRGB { + return PaletteRGBImpl.from(source); + }, +}); + +/** + * A {@link Palette} representing RGB swatch values. + * @public + */ +class PaletteRGBImpl implements Palette { + /** + * {@inheritdoc Palette.source} + */ + public readonly source: SwatchRGB; + public readonly swatches: ReadonlyArray; + private lastIndex: number; + private reversedSwatches: ReadonlyArray; + /** + * + * @param source - The source color for the palette + * @param swatches - All swatches in the palette + */ + constructor(source: SwatchRGB, swatches: ReadonlyArray) { + this.source = source; + this.swatches = swatches; + + this.reversedSwatches = Object.freeze([...this.swatches].reverse()); + this.lastIndex = this.swatches.length - 1; + } + + /** + * {@inheritdoc Palette.colorContrast} + */ + public colorContrast( + reference: Swatch, + contrastTarget: number, + initialSearchIndex?: number, + direction?: 1 | -1, + ): SwatchRGB { + if (initialSearchIndex === undefined) { + initialSearchIndex = this.closestIndexOf(reference); + } + + let source: ReadonlyArray = this.swatches; + const endSearchIndex = this.lastIndex; + let startSearchIndex = initialSearchIndex; + + if (direction === undefined) { + direction = directionByIsDark(reference); + } + + const condition = (value: SwatchRGB) => contrast(reference, value) >= contrastTarget; + + if (direction === -1) { + source = this.reversedSwatches; + startSearchIndex = endSearchIndex - startSearchIndex; + } + + return binarySearch(source, condition, startSearchIndex, endSearchIndex); + } + + /** + * {@inheritdoc Palette.get} + */ + public get(index: number): SwatchRGB { + return this.swatches[index] || this.swatches[clamp(index, 0, this.lastIndex)]; + } + + /** + * {@inheritdoc Palette.closestIndexOf} + */ + public closestIndexOf(reference: Swatch): number { + const index = this.swatches.indexOf(reference as SwatchRGB); + + if (index !== -1) { + return index; + } + + const closest = this.swatches.reduce((previous, next) => + Math.abs(next.relativeLuminance - reference.relativeLuminance) < + Math.abs(previous.relativeLuminance - reference.relativeLuminance) + ? next + : previous, + ); + + return this.swatches.indexOf(closest); + } + + /** + * Create a color palette from a provided swatch + * @param source - The source swatch to create a palette from + * @returns + */ + static from(source: SwatchRGB): PaletteRGB { + return new PaletteRGBImpl( + source, + Object.freeze( + new ComponentStateColorPalette({ + baseColor: ColorRGBA64.fromObject(source)!, + }).palette.map(x => { + const _x = parseColorHexRGB(x.toStringHexRGB())!; + return SwatchRGB.create(_x.r, _x.g, _x.b); + }), + ), + ); + } +} diff --git a/packages/web-components/src/color-vNext/recipes/accent-fill.ts b/packages/web-components/src/color-vNext/recipes/accent-fill.ts new file mode 100644 index 0000000000000..8beaccb8bd807 --- /dev/null +++ b/packages/web-components/src/color-vNext/recipes/accent-fill.ts @@ -0,0 +1,54 @@ +import { inRange } from 'lodash-es'; +import { PaletteRGB } from '../palette'; +import { Swatch } from '../swatch'; +import { isDark } from '../utilities/is-dark'; + +/** + * @internal + */ +export function accentFill( + palette: PaletteRGB, + neutralPaletteRGB: PaletteRGB, + reference: Swatch, + textColor: Swatch, + contrastTarget: number, + hoverDelta: number, + activeDelta: number, + focusDelta: number, + selectedDelta: number, + neutralFillRestDelta: number, + neutralFillHoverDelta: number, + neutralFillActiveDelta: number, +) { + const accent = palette.source; + const referenceIndex = neutralPaletteRGB.closestIndexOf(reference); + const swapThreshold = Math.max(neutralFillRestDelta, neutralFillHoverDelta, neutralFillActiveDelta); + const direction = referenceIndex >= swapThreshold ? -1 : 1; + const paletteLength = palette.swatches.length; + const maxIndex = paletteLength - 1; + const accentIndex = palette.closestIndexOf(accent); + let accessibleOffset = 0; + + while ( + accessibleOffset < direction * hoverDelta && + inRange(accentIndex + accessibleOffset + direction, 0, paletteLength) && + textColor.contrast(palette.get(accentIndex + accessibleOffset + direction)) >= contrastTarget && + inRange(accentIndex + accessibleOffset + direction + direction, 0, maxIndex) + ) { + accessibleOffset += direction; + } + + const hoverIndex = accentIndex + accessibleOffset; + const restIndex = hoverIndex + direction * -1 * hoverDelta; + const activeIndex = restIndex + direction * activeDelta; + const focusIndex = restIndex + direction * focusDelta; + const selectedIndex = restIndex + (isDark(reference) ? selectedDelta * -1 : selectedDelta); + + return { + rest: palette.get(restIndex), + hover: palette.get(hoverIndex), + active: palette.get(activeIndex), + focus: palette.get(focusIndex), + selected: palette.get(selectedIndex), + }; +} diff --git a/packages/web-components/src/color-vNext/recipes/accent-foreground-cut.ts b/packages/web-components/src/color-vNext/recipes/accent-foreground-cut.ts new file mode 100644 index 0000000000000..3569a795ebbc2 --- /dev/null +++ b/packages/web-components/src/color-vNext/recipes/accent-foreground-cut.ts @@ -0,0 +1,9 @@ +import { Swatch } from '../swatch'; +import { black, white } from '../utilities/color-constants'; + +/** + * @internal + */ +export function accentForegroundCut(reference: Swatch, contrastTarget: number) { + return reference.contrast(white) >= contrastTarget ? white : black; +} diff --git a/packages/web-components/src/color-vNext/recipes/accent-foreground.ts b/packages/web-components/src/color-vNext/recipes/accent-foreground.ts new file mode 100644 index 0000000000000..98baddf91572d --- /dev/null +++ b/packages/web-components/src/color-vNext/recipes/accent-foreground.ts @@ -0,0 +1,45 @@ +import { PaletteRGB } from '../palette'; +import { Swatch } from '../swatch'; +import { directionByIsDark } from '../utilities/direction-by-is-dark'; + +/** + * @internal + */ +export function accentForeground( + palette: PaletteRGB, + reference: Swatch, + contrastTarget: number, + restDelta: number, + hoverDelta: number, + activeDelta: number, + focusDelta: number, +) { + const accent = palette.source; + const accentIndex = palette.closestIndexOf(accent); + const direction = directionByIsDark(reference); + const startIndex = + accentIndex + + (direction === 1 ? Math.min(restDelta, hoverDelta) : Math.max(direction * restDelta, direction * hoverDelta)); + const accessibleSwatch = palette.colorContrast(reference, contrastTarget, startIndex, direction); + const accessibleIndex1 = palette.closestIndexOf(accessibleSwatch); + const accessibleIndex2 = accessibleIndex1 + direction * Math.abs(restDelta - hoverDelta); + const indexOneIsRestState = direction === 1 ? restDelta < hoverDelta : direction * restDelta > direction * hoverDelta; + + let restIndex: number; + let hoverIndex: number; + + if (indexOneIsRestState) { + restIndex = accessibleIndex1; + hoverIndex = accessibleIndex2; + } else { + restIndex = accessibleIndex2; + hoverIndex = accessibleIndex1; + } + + return { + rest: palette.get(restIndex), + hover: palette.get(hoverIndex), + active: palette.get(restIndex + direction * activeDelta), + focus: palette.get(restIndex + direction * focusDelta), + }; +} diff --git a/packages/web-components/src/color-vNext/recipes/neutral-divider.ts b/packages/web-components/src/color-vNext/recipes/neutral-divider.ts new file mode 100644 index 0000000000000..88ed5a7b29d9e --- /dev/null +++ b/packages/web-components/src/color-vNext/recipes/neutral-divider.ts @@ -0,0 +1,15 @@ +import { Swatch } from '../swatch'; +import { PaletteRGB } from '../palette'; +import { directionByIsDark } from '../utilities/direction-by-is-dark'; + +/** + * The neutralDivider color recipe + * @param palette - The palette to operate on + * @param reference - The reference color + * @param delta - The offset from the reference + * + * @internal + */ +export function neutralDivider(palette: PaletteRGB, reference: Swatch, delta: number) { + return palette.get(palette.closestIndexOf(reference) + directionByIsDark(reference) * delta); +} diff --git a/packages/web-components/src/color-vNext/recipes/neutral-fill-card.ts b/packages/web-components/src/color-vNext/recipes/neutral-fill-card.ts new file mode 100644 index 0000000000000..d55f2fb27fab3 --- /dev/null +++ b/packages/web-components/src/color-vNext/recipes/neutral-fill-card.ts @@ -0,0 +1,11 @@ +import { PaletteRGB } from '../palette'; +import { Swatch } from '../swatch'; + +/** + * @internal + */ +export function neutralFillCard(palette: PaletteRGB, reference: Swatch, delta: number) { + const referenceIndex = palette.closestIndexOf(reference); + + return palette.get(referenceIndex - (referenceIndex < delta ? delta * -1 : delta)); +} diff --git a/packages/web-components/src/color-vNext/recipes/neutral-fill-input.ts b/packages/web-components/src/color-vNext/recipes/neutral-fill-input.ts new file mode 100644 index 0000000000000..51984d0b28144 --- /dev/null +++ b/packages/web-components/src/color-vNext/recipes/neutral-fill-input.ts @@ -0,0 +1,27 @@ +import { PaletteRGB } from '../palette'; +import { Swatch } from '../swatch'; +import { directionByIsDark } from '../utilities/direction-by-is-dark'; + +/** + * @internal + */ +export function neutralFillInput( + palette: PaletteRGB, + reference: Swatch, + restDelta: number, + hoverDelta: number, + activeDelta: number, + focusDelta: number, + selectedDelta: number, +) { + const direction = directionByIsDark(reference); + const referenceIndex = palette.closestIndexOf(reference); + + return { + rest: palette.get(referenceIndex - direction * restDelta), + hover: palette.get(referenceIndex - direction * hoverDelta), + active: palette.get(referenceIndex - direction * activeDelta), + focus: palette.get(referenceIndex - direction * focusDelta), + selected: palette.get(referenceIndex - direction * selectedDelta), + }; +} diff --git a/packages/web-components/src/color-vNext/recipes/neutral-fill-stealth.ts b/packages/web-components/src/color-vNext/recipes/neutral-fill-stealth.ts new file mode 100644 index 0000000000000..bca50d1403a64 --- /dev/null +++ b/packages/web-components/src/color-vNext/recipes/neutral-fill-stealth.ts @@ -0,0 +1,41 @@ +import { PaletteRGB } from '../palette'; +import { Swatch } from '../swatch'; + +/** + * @internal + */ +export function neutralFillStealth( + palette: PaletteRGB, + reference: Swatch, + restDelta: number, + hoverDelta: number, + activeDelta: number, + focusDelta: number, + selectedDelta: number, + fillRestDelta: number, + fillHoverDelta: number, + fillActiveDelta: number, + fillFocusDelta: number, +) { + const swapThreshold = Math.max( + restDelta, + hoverDelta, + activeDelta, + focusDelta, + fillRestDelta, + fillHoverDelta, + fillActiveDelta, + fillFocusDelta, + ); + + const referenceIndex = palette.closestIndexOf(reference); + const direction: 1 | -1 = referenceIndex >= swapThreshold ? -1 : 1; + + return { + rest: palette.get(referenceIndex + direction * restDelta), + hover: palette.get(referenceIndex + direction * hoverDelta), + active: palette.get(referenceIndex + direction * activeDelta), + focus: palette.get(referenceIndex + direction * focusDelta), + selected: palette.get(referenceIndex + direction * selectedDelta), + }; +} diff --git a/packages/web-components/src/color-vNext/recipes/neutral-fill-toggle.ts b/packages/web-components/src/color-vNext/recipes/neutral-fill-toggle.ts new file mode 100644 index 0000000000000..f96e1dce1cc49 --- /dev/null +++ b/packages/web-components/src/color-vNext/recipes/neutral-fill-toggle.ts @@ -0,0 +1,37 @@ +import { PaletteRGB } from '../palette'; +import { Swatch } from '../swatch'; +import { directionByIsDark } from '../utilities/direction-by-is-dark'; + +/** + * @internal + */ +export function neutralFillToggle( + palette: PaletteRGB, + reference: Swatch, + restDelta: number, + hoverDelta: number, + activeDelta: number, + focusDelta: number, +) { + const direction = directionByIsDark(reference); + const accessibleIndex = palette.closestIndexOf(palette.colorContrast(reference, 4.5)); + const accessibleIndex2 = accessibleIndex + direction * Math.abs(restDelta - hoverDelta); + const indexOneIsRest = direction === 1 ? restDelta < hoverDelta : direction * restDelta > direction * hoverDelta; + let restIndex: number; + let hoverIndex: number; + + if (indexOneIsRest) { + restIndex = accessibleIndex; + hoverIndex = accessibleIndex2; + } else { + restIndex = accessibleIndex2; + hoverIndex = accessibleIndex; + } + + return { + rest: palette.get(restIndex), + hover: palette.get(hoverIndex), + active: palette.get(restIndex + direction * activeDelta), + focus: palette.get(restIndex + direction * focusDelta), + }; +} diff --git a/packages/web-components/src/color-vNext/recipes/neutral-fill.ts b/packages/web-components/src/color-vNext/recipes/neutral-fill.ts new file mode 100644 index 0000000000000..3e8bef1f5e6b1 --- /dev/null +++ b/packages/web-components/src/color-vNext/recipes/neutral-fill.ts @@ -0,0 +1,34 @@ +import { PaletteRGB } from '../palette'; +import { Swatch } from '../swatch'; + +/** + * + * @param palette - The palette to operate on + * @param reference - The reference color to calculate a color for + * @param delta - The offset from the reference's location + * @param threshold - Determines if a lighter or darker color than the reference will be picked. + * @returns + * + * @internal + */ +export function neutralFill( + palette: PaletteRGB, + reference: Swatch, + restDelta: number, + hoverDelta: number, + activeDelta: number, + focusDelta: number, + selectedDelta: number, +) { + const referenceIndex = palette.closestIndexOf(reference); + const threshold = Math.max(restDelta, hoverDelta, activeDelta, focusDelta); + const direction = referenceIndex >= threshold ? -1 : 1; + + return { + rest: palette.get(referenceIndex + direction * restDelta), + hover: palette.get(referenceIndex + direction * hoverDelta), + active: palette.get(referenceIndex + direction * activeDelta), + focus: palette.get(referenceIndex + direction * focusDelta), + selected: palette.get(referenceIndex + direction * selectedDelta), + }; +} diff --git a/packages/web-components/src/color-vNext/recipes/neutral-focus-inner-accent.ts b/packages/web-components/src/color-vNext/recipes/neutral-focus-inner-accent.ts new file mode 100644 index 0000000000000..933e901e6995e --- /dev/null +++ b/packages/web-components/src/color-vNext/recipes/neutral-focus-inner-accent.ts @@ -0,0 +1,12 @@ +import { PaletteRGB } from '../palette'; +import { Swatch } from '../swatch'; +import { directionByIsDark } from '../utilities/direction-by-is-dark'; + +export function neutralFocusInnerAccent(palette: PaletteRGB, reference: Swatch, focusColor: Swatch) { + return palette.colorContrast( + focusColor, + 3.5, + palette.closestIndexOf(palette.source), + (directionByIsDark(reference) * -1) as 1 | -1, + ); +} diff --git a/packages/web-components/src/color-vNext/recipes/neutral-focus.ts b/packages/web-components/src/color-vNext/recipes/neutral-focus.ts new file mode 100644 index 0000000000000..c1f84cbf5625b --- /dev/null +++ b/packages/web-components/src/color-vNext/recipes/neutral-focus.ts @@ -0,0 +1,9 @@ +import { PaletteRGB } from '../palette'; +import { Swatch } from '../swatch'; + +/** + * @internal + */ +export function neutralFocus(palette: PaletteRGB, reference: Swatch) { + return palette.colorContrast(reference, 3.5); +} diff --git a/packages/web-components/src/color-vNext/recipes/neutral-foreground-hint.ts b/packages/web-components/src/color-vNext/recipes/neutral-foreground-hint.ts new file mode 100644 index 0000000000000..981e01aa9d5f0 --- /dev/null +++ b/packages/web-components/src/color-vNext/recipes/neutral-foreground-hint.ts @@ -0,0 +1,13 @@ +import { Swatch } from '../swatch'; +import { PaletteRGB } from '../palette'; + +/** + * The neutralForegroundHint color recipe + * @param palette - The palette to operate on + * @param reference - The reference color + * + * @internal + */ +export function neutralForegroundHint(palette: PaletteRGB, reference: Swatch) { + return palette.colorContrast(reference, 4.5); +} diff --git a/packages/web-components/src/color-vNext/recipes/neutral-foreground.ts b/packages/web-components/src/color-vNext/recipes/neutral-foreground.ts new file mode 100644 index 0000000000000..35c64910f122e --- /dev/null +++ b/packages/web-components/src/color-vNext/recipes/neutral-foreground.ts @@ -0,0 +1,9 @@ +import { PaletteRGB } from '../palette'; +import { Swatch } from '../swatch'; + +/** + * @internal + */ +export function neutralForeground(palette: PaletteRGB, reference: Swatch) { + return palette.colorContrast(reference, 14); +} diff --git a/packages/web-components/src/color-vNext/recipes/neutral-layer-L1.ts b/packages/web-components/src/color-vNext/recipes/neutral-layer-L1.ts new file mode 100644 index 0000000000000..f0aa2cc3cc5a4 --- /dev/null +++ b/packages/web-components/src/color-vNext/recipes/neutral-layer-L1.ts @@ -0,0 +1,7 @@ +import { PaletteRGB } from '../palette'; +import { Swatch } from '../swatch'; +import { baseLayerLuminanceSwatch } from '../utilities/base-layer-luminance'; + +export function neutralLayerL1(palette: PaletteRGB, baseLayerLuminance: number) { + return palette.get(palette.closestIndexOf(baseLayerLuminanceSwatch(baseLayerLuminance))); +} diff --git a/packages/web-components/src/color-vNext/recipes/neutral-layer-L2.ts b/packages/web-components/src/color-vNext/recipes/neutral-layer-L2.ts new file mode 100644 index 0000000000000..f7b4768b72a0d --- /dev/null +++ b/packages/web-components/src/color-vNext/recipes/neutral-layer-L2.ts @@ -0,0 +1,31 @@ +import { PaletteRGB } from '../palette'; +import { baseLayerLuminanceSwatch } from '../utilities/base-layer-luminance'; + +export function neutralLayerL2Index( + palette: PaletteRGB, + luminance: number, + cardDelta: number, + fillRestDelta: number, + fillHoverDelta: number, + fillActiveDelta: number, +) { + return Math.max( + palette.closestIndexOf(baseLayerLuminanceSwatch(luminance)) + cardDelta, + fillRestDelta, + fillHoverDelta, + fillActiveDelta, + ); +} + +export function neutralLayerL2( + palette: PaletteRGB, + luminance: number, + cardDelta: number, + fillRestDelta: number, + fillHoverDelta: number, + fillActiveDelta: number, +) { + return palette.get( + neutralLayerL2Index(palette, luminance, cardDelta, fillRestDelta, fillHoverDelta, fillActiveDelta), + ); +} diff --git a/packages/web-components/src/color-vNext/recipes/neutral-layer-L3.ts b/packages/web-components/src/color-vNext/recipes/neutral-layer-L3.ts new file mode 100644 index 0000000000000..f0416e0ed1dfc --- /dev/null +++ b/packages/web-components/src/color-vNext/recipes/neutral-layer-L3.ts @@ -0,0 +1,15 @@ +import { PaletteRGB } from '../palette'; +import { neutralLayerL2Index } from './neutral-layer-L2'; + +export function neutralLayerL3( + palette: PaletteRGB, + luminance: number, + cardDelta: number, + fillRestDelta: number, + fillHoverDelta: number, + fillActiveDelta: number, +) { + return palette.get( + neutralLayerL2Index(palette, luminance, cardDelta, fillRestDelta, fillHoverDelta, fillActiveDelta) + cardDelta, + ); +} diff --git a/packages/web-components/src/color-vNext/recipes/neutral-layer-L4.ts b/packages/web-components/src/color-vNext/recipes/neutral-layer-L4.ts new file mode 100644 index 0000000000000..7b75a75668cd7 --- /dev/null +++ b/packages/web-components/src/color-vNext/recipes/neutral-layer-L4.ts @@ -0,0 +1,15 @@ +import { PaletteRGB } from '../palette'; +import { neutralLayerL2Index } from './neutral-layer-L2'; + +export function neutralLayerL4( + palette: PaletteRGB, + luminance: number, + cardDelta: number, + fillRestDelta: number, + fillHoverDelta: number, + fillActiveDelta: number, +) { + return palette.get( + neutralLayerL2Index(palette, luminance, cardDelta, fillRestDelta, fillHoverDelta, fillActiveDelta) + cardDelta * 2, + ); +} diff --git a/packages/web-components/src/color-vNext/recipes/neutral-layer-card.ts b/packages/web-components/src/color-vNext/recipes/neutral-layer-card.ts new file mode 100644 index 0000000000000..f050d507bb8fe --- /dev/null +++ b/packages/web-components/src/color-vNext/recipes/neutral-layer-card.ts @@ -0,0 +1,9 @@ +import { PaletteRGB } from '../palette'; +import { baseLayerLuminanceSwatch } from '../utilities/base-layer-luminance'; + +/** + * @internal + */ +export function neutralLayerCard(palette: PaletteRGB, relativeLuminance: number, cardDelta: number) { + return palette.get(palette.closestIndexOf(baseLayerLuminanceSwatch(relativeLuminance)) - cardDelta); +} diff --git a/packages/web-components/src/color-vNext/recipes/neutral-layer-floating.ts b/packages/web-components/src/color-vNext/recipes/neutral-layer-floating.ts new file mode 100644 index 0000000000000..cb90ef69bdde6 --- /dev/null +++ b/packages/web-components/src/color-vNext/recipes/neutral-layer-floating.ts @@ -0,0 +1,10 @@ +import { PaletteRGB } from '../palette'; +import { neutralLayerCard } from './neutral-layer-card'; + +/** + * @internal + */ +export function neutralLayerFloating(palette: PaletteRGB, relativeLuminance: number, cardDelta: number) { + const cardIndex = palette.closestIndexOf(neutralLayerCard(palette, relativeLuminance, cardDelta)); + return palette.get(cardIndex - cardDelta); +} diff --git a/packages/web-components/src/color-vNext/recipes/neutral-outline.ts b/packages/web-components/src/color-vNext/recipes/neutral-outline.ts new file mode 100644 index 0000000000000..d014948659476 --- /dev/null +++ b/packages/web-components/src/color-vNext/recipes/neutral-outline.ts @@ -0,0 +1,30 @@ +import { PaletteRGB } from '../palette'; +import { Swatch } from '../swatch'; +import { directionByIsDark } from '../utilities/direction-by-is-dark'; + +/** + * @internal + */ +export function neutralOutline( + palette: PaletteRGB, + reference: Swatch, + restDelta: number, + hoverDelta: number, + activeDelta: number, + focusDelta: number, +) { + const referenceIndex = palette.closestIndexOf(reference); + const direction = directionByIsDark(reference); + + const restIndex = referenceIndex + direction * restDelta; + const hoverIndex = restIndex + direction * (hoverDelta - restDelta); + const activeIndex = restIndex + direction * (activeDelta - restDelta); + const focusIndex = restIndex + direction * (focusDelta - restDelta); + + return { + rest: palette.get(restIndex), + hover: palette.get(hoverIndex), + active: palette.get(activeIndex), + focus: palette.get(focusIndex), + }; +} diff --git a/packages/web-components/src/color-vNext/swatch.ts b/packages/web-components/src/color-vNext/swatch.ts new file mode 100644 index 0000000000000..3e761f41a6f7d --- /dev/null +++ b/packages/web-components/src/color-vNext/swatch.ts @@ -0,0 +1,50 @@ +import { ColorRGBA64, rgbToRelativeLuminance } from '@microsoft/fast-colors'; +import { contrast, RelativeLuminance } from './utilities/relative-luminance'; + +/** + * Represents a color in a {@link Palette} + * @public + */ +export interface Swatch extends RelativeLuminance { + toColorString(): string; + contrast(target: RelativeLuminance): number; +} + +export interface SwatchRGB extends Swatch { + r: number; + g: number; + b: number; +} + +export const SwatchRGB = Object.freeze({ + create(r: number, g: number, b: number): SwatchRGB { + return new SwatchRGBImpl(r, g, b); + }, +}); + +/** + * A RGB implementation of {@link Swatch} + * @public + */ +class SwatchRGBImpl extends ColorRGBA64 implements Swatch { + readonly relativeLuminance: number; + + /** + * + * @param red - Red channel expressed as a number between 0 and 1 + * @param green - Green channel expressed as a number between 0 and 1 + * @param blue - Blue channel expressed as a number between 0 and 1 + */ + constructor(red: number, green: number, blue: number) { + super(red, green, blue, 1); + this.relativeLuminance = rgbToRelativeLuminance(this); + } + + public toColorString = this.toStringHexRGB; + public contrast = contrast.bind(null, this); + public createCSS = this.toColorString; + + static fromObject(obj: { r: number; g: number; b: number }) { + return new SwatchRGBImpl(obj.r, obj.g, obj.b); + } +} diff --git a/packages/web-components/src/color-vNext/utilities/base-layer-luminance.ts b/packages/web-components/src/color-vNext/utilities/base-layer-luminance.ts new file mode 100644 index 0000000000000..0345451adf360 --- /dev/null +++ b/packages/web-components/src/color-vNext/utilities/base-layer-luminance.ts @@ -0,0 +1,5 @@ +import { SwatchRGB } from '../swatch'; + +export function baseLayerLuminanceSwatch(luminance: number) { + return SwatchRGB.create(luminance, luminance, luminance); +} diff --git a/packages/web-components/src/color-vNext/utilities/binary-search.ts b/packages/web-components/src/color-vNext/utilities/binary-search.ts new file mode 100644 index 0000000000000..1cf193f15d231 --- /dev/null +++ b/packages/web-components/src/color-vNext/utilities/binary-search.ts @@ -0,0 +1,31 @@ +/** + * @internal + */ +export function binarySearch( + valuesToSearch: T[] | ReadonlyArray, + searchCondition: (value: T) => boolean, + startIndex: number = 0, + endIndex: number = valuesToSearch.length - 1, +): T { + if (endIndex === startIndex) { + return valuesToSearch[startIndex]; + } + + const middleIndex: number = Math.floor((endIndex - startIndex) / 2) + startIndex; + + // Check to see if this passes on the item in the center of the array + // if it does check the previous values + return searchCondition(valuesToSearch[middleIndex]) + ? binarySearch( + valuesToSearch, + searchCondition, + startIndex, + middleIndex, // include this index because it passed the search condition + ) + : binarySearch( + valuesToSearch, + searchCondition, + middleIndex + 1, // exclude this index because it failed the search condition + endIndex, + ); +} diff --git a/packages/web-components/src/color-vNext/utilities/color-constants.ts b/packages/web-components/src/color-vNext/utilities/color-constants.ts new file mode 100644 index 0000000000000..9e17f28aa9a05 --- /dev/null +++ b/packages/web-components/src/color-vNext/utilities/color-constants.ts @@ -0,0 +1,23 @@ +import { parseColorHexRGB } from '@microsoft/fast-colors'; +import { SwatchRGB } from '../swatch'; + +/** + * @internal + */ +export const white = SwatchRGB.create(1, 1, 1); +/** + * @internal + */ +export const black = SwatchRGB.create(0, 0, 0); + +/** + * @internal + */ +export const middleGrey = SwatchRGB.create(0.5, 0.5, 0.5); + +/** + * @internal + */ + +const base = parseColorHexRGB('#0078D4')!; +export const accentBase = SwatchRGB.create(base.r, base.g, base.b); diff --git a/packages/web-components/src/color-vNext/utilities/direction-by-is-dark.ts b/packages/web-components/src/color-vNext/utilities/direction-by-is-dark.ts new file mode 100644 index 0000000000000..6c31b711d563e --- /dev/null +++ b/packages/web-components/src/color-vNext/utilities/direction-by-is-dark.ts @@ -0,0 +1,9 @@ +import { Swatch } from '../swatch'; +import { isDark } from './is-dark'; + +/** + * @internal + */ +export function directionByIsDark(color: Swatch): 1 | -1 { + return isDark(color) ? -1 : 1; +} diff --git a/packages/web-components/src/color-vNext/utilities/is-dark.ts b/packages/web-components/src/color-vNext/utilities/is-dark.ts new file mode 100644 index 0000000000000..9a749dcaa237f --- /dev/null +++ b/packages/web-components/src/color-vNext/utilities/is-dark.ts @@ -0,0 +1,20 @@ +import { Swatch } from '../swatch'; + +/* + * A color is in "dark" if there is more contrast between #000000 and a reference + * color than #FFFFFF and the reference color. That threshold can be expressed as a relative luminance + * using the contrast formula as (1 + 0.5) / (R + 0.05) === (R + 0.05) / (0 + 0.05), + * which reduces to the following, where 'R' is the relative luminance of the reference color + */ +const target = (-0.1 + Math.sqrt(0.21)) / 2; + +/** + * Determines if a color should be considered Dark Mode + * @param color - The color to check to mode of + * @returns boolean + * + * @internal + */ +export function isDark(color: Swatch): boolean { + return color.relativeLuminance <= target; +} diff --git a/packages/web-components/src/color-vNext/utilities/relative-luminance.ts b/packages/web-components/src/color-vNext/utilities/relative-luminance.ts new file mode 100644 index 0000000000000..e530348e1ed29 --- /dev/null +++ b/packages/web-components/src/color-vNext/utilities/relative-luminance.ts @@ -0,0 +1,19 @@ +/** + * @public + */ +export interface RelativeLuminance { + /** + * A number between 0 and 1, calculated by {@link https://www.w3.org/WAI/GL/wiki/Relative_luminance} + */ + readonly relativeLuminance: number; +} + +/** + * @internal + */ +export function contrast(a: RelativeLuminance, b: RelativeLuminance): number { + const L1 = a.relativeLuminance > b.relativeLuminance ? a : b; + const L2 = a.relativeLuminance > b.relativeLuminance ? b : a; + + return (L1.relativeLuminance + 0.05) / (L2.relativeLuminance + 0.05); +} diff --git a/packages/web-components/src/color/accent-fill.spec.ts b/packages/web-components/src/color/accent-fill.spec.ts index a1587bcd2d0f9..e0a291f761cab 100644 --- a/packages/web-components/src/color/accent-fill.spec.ts +++ b/packages/web-components/src/color/accent-fill.spec.ts @@ -1,12 +1,12 @@ import { expect } from 'chai'; import { - accentBaseColor, accentPalette as getAccentPalette, DesignSystem, DesignSystemDefaults, neutralPalette as getNeutralPalette, } from '../fluent-design-system'; import { + accentFill, accentFillActive, accentFillHover, accentFillLargeActive, @@ -18,6 +18,12 @@ import { } from './accent-fill'; import { findClosestSwatchIndex, Palette } from './palette'; import { contrast, Swatch } from './common'; +import { parseColorHexRGB } from "@microsoft/fast-colors"; +import { neutralBaseColor, accentBaseColor } from "./color-constants"; +import { PaletteRGB } from "../color-vNext/palette"; +import { SwatchRGB } from "../color-vNext/swatch"; +import { accentFill as accentFillNew } from "../color-vNext/recipes/accent-fill"; +import { accentForegroundCut as accentForegroundCutNew } from '../color-vNext/recipes/accent-foreground-cut'; import { accentForegroundCut } from './accent-foreground-cut'; describe('accentFill', (): void => { @@ -26,7 +32,7 @@ describe('accentFill', (): void => { const accentIndex: number = findClosestSwatchIndex( getAccentPalette, - accentBaseColor(DesignSystemDefaults), + accentBaseColor, )(DesignSystemDefaults); it('should operate on design system defaults', (): void => { @@ -69,3 +75,44 @@ describe('accentFill', (): void => { }); }); }); + +describe("ensure parity between old and new recipe implementation", () => { + const neutralColor = (parseColorHexRGB(neutralBaseColor)!) + const neutralPalette = PaletteRGB.create(SwatchRGB.create(neutralColor.r, neutralColor.g, neutralColor.b)); + const accentColor = (parseColorHexRGB(accentBaseColor)!) + const accentPalette = PaletteRGB.create(SwatchRGB.create(accentColor.r, accentColor.g, accentColor.b)); + neutralPalette.swatches.forEach(( newSwatch, index ) => { + const { + accentFillHoverDelta, + accentFillActiveDelta, + accentFillFocusDelta, + accentFillSelectedDelta, + neutralFillRestDelta, + neutralFillHoverDelta, + neutralFillActiveDelta, + } = DesignSystemDefaults; + + const oldValues = accentFill({...DesignSystemDefaults, backgroundColor: DesignSystemDefaults.neutralPalette[index]}); + const textColor = accentForegroundCutNew(accentPalette.source, 4.5); + const newValues = accentFillNew( + accentPalette, + neutralPalette, + newSwatch, + textColor, + 4.5, + accentFillHoverDelta, + accentFillActiveDelta, + accentFillFocusDelta, + accentFillSelectedDelta, + neutralFillRestDelta, + neutralFillHoverDelta, + neutralFillActiveDelta + ) + + for (let key in oldValues) { + it(`${newSwatch.toColorString()}old value for ${key} at ${oldValues[key]} should be equal to new value`, () => { + expect(oldValues[key]).to.equal(newValues[key].toColorString().toUpperCase()) + } ) + } + }) +}) diff --git a/packages/web-components/src/color/accent-foreground-cut.spec.ts b/packages/web-components/src/color/accent-foreground-cut.spec.ts index cdd144287fb6d..4a013a97c0ba0 100644 --- a/packages/web-components/src/color/accent-foreground-cut.spec.ts +++ b/packages/web-components/src/color/accent-foreground-cut.spec.ts @@ -1,7 +1,12 @@ +import { parseColorHexRGB } from "@microsoft/fast-colors"; import { expect } from 'chai'; +import { PaletteRGB } from "../color-vNext/palette"; +import { SwatchRGB } from "../color-vNext/swatch"; import { DesignSystemDefaults, DesignSystem } from '../fluent-design-system'; import { accentForegroundCut, accentForegroundCutLarge } from './accent-foreground-cut'; -import { Swatch } from './common'; +import { neutralBaseColor, accentBaseColor } from "./color-constants"; +import { Swatch } from "./common"; +import { accentForegroundCut as accentForegroundCutNew } from "../color-vNext/recipes/accent-foreground-cut"; describe('Cut text', (): void => { it('should return white by by default', (): void => { @@ -22,3 +27,19 @@ describe('Cut text', (): void => { ).to.equal('#000000'); }); }); +describe("ensure parity between old and new recipe implementation", () => { + const color = (parseColorHexRGB(accentBaseColor)!) + const palette = PaletteRGB.create(SwatchRGB.create(color.r, color.g, color.b)); + it( + `should be the same for ${palette.source}`, + () => { + expect( + accentForegroundCut( + { ...DesignSystemDefaults, backgroundColor: DesignSystemDefaults.accentBaseColor } + ) + ).to.be.equal( + accentForegroundCutNew(palette.source, 4.5).toColorString().toUpperCase() + ) + } + ) +}) diff --git a/packages/web-components/src/color/accent-foreground.spec.ts b/packages/web-components/src/color/accent-foreground.spec.ts index 07ed01a6dd6b9..1bca007eb5eec 100644 --- a/packages/web-components/src/color/accent-foreground.spec.ts +++ b/packages/web-components/src/color/accent-foreground.spec.ts @@ -13,9 +13,14 @@ import { accentForegroundLargeHover, accentForegroundLargeRest, accentForegroundRest, + accentForeground } from './accent-foreground'; import { Palette } from './palette'; import { contrast, Swatch } from './common'; +import { accentBaseColor, neutralBaseColor } from "./color-constants"; +import { PaletteRGB } from "../color-vNext/palette"; +import { SwatchRGB } from "../color-vNext/swatch"; +import { accentForeground as accentForegroundNew } from "../color-vNext/recipes/accent-foreground"; describe('accentForeground', (): void => { const neutralPalette: Palette = getNeutralPalette(DesignSystemDefaults); @@ -93,3 +98,39 @@ describe('accentForeground', (): void => { ); }); }); +describe("ensure parity between old and new recipe implementation", () => { + const neutralBase = parseColorHexRGB(neutralBaseColor)!; + const accentBase = parseColorHexRGB(accentBaseColor)!; + + const neutralPalette = PaletteRGB.create(SwatchRGB.create(neutralBase.r, neutralBase.g, neutralBase.b)); + const accentPalette = PaletteRGB.create(SwatchRGB.create(accentBase.r, accentBase.g, accentBase.b)); + + neutralPalette.swatches.forEach((newSwatch, index) => { + const { + accentForegroundRestDelta, + accentForegroundFocusDelta, + accentForegroundActiveDelta, + accentForegroundHoverDelta + } = DesignSystemDefaults; + const oldValues = accentForeground({ + ...DesignSystemDefaults, + backgroundColor: DesignSystemDefaults.neutralPalette[index], + }); + const newValues = accentForegroundNew( + accentPalette, + newSwatch, + 4.5, + accentForegroundRestDelta, + accentForegroundHoverDelta, + accentForegroundActiveDelta, + accentForegroundFocusDelta, + ); + it(`should be the same for ${newSwatch}`, () => { + for (let key in newValues) { + expect(oldValues[key]).to.equal( + newValues[key].toColorString().toUpperCase() + ); + } + }); + }); +}); diff --git a/packages/web-components/src/color/neutral-divider.spec.ts b/packages/web-components/src/color/neutral-divider.spec.ts index 289674ebeb0b9..5facf632d7e0f 100644 --- a/packages/web-components/src/color/neutral-divider.spec.ts +++ b/packages/web-components/src/color/neutral-divider.spec.ts @@ -1,5 +1,10 @@ +import { ColorRGBA64, parseColorHexRGB } from "@microsoft/fast-colors"; import { expect } from 'chai'; +import { PaletteRGB } from "../color-vNext/palette"; +import { neutralDivider } from "../color-vNext/recipes/neutral-divider"; +import { SwatchRGB } from "../color-vNext/swatch"; import { DesignSystemDefaults } from '../fluent-design-system'; +import { neutralBaseColor } from "./color-constants"; import { neutralDividerRest } from './neutral-divider'; describe('neutralDividerRest', (): void => { @@ -11,3 +16,14 @@ describe('neutralDividerRest', (): void => { expect(typeof neutralDividerRest(() => '#FFF')).to.equal('function'); }); }); +describe("ensure parity between old and new recipe implementation", () => { + const color = (parseColorHexRGB(neutralBaseColor)!) + const palette = PaletteRGB.create(SwatchRGB.create(color.r, color.g, color.b)); + palette.swatches.forEach(( newSwatch, index ) => { + it(`should be the same for ${newSwatch}`, () => { + expect(neutralDivider(palette, newSwatch, DesignSystemDefaults.neutralDividerRestDelta).toColorString().toUpperCase()).to.equal( + neutralDividerRest({...DesignSystemDefaults, backgroundColor: DesignSystemDefaults.neutralPalette[index]}) + ) + }) + }) +}) diff --git a/packages/web-components/src/color/neutral-fill-card.spec.ts b/packages/web-components/src/color/neutral-fill-card.spec.ts index fbd27a12f5d80..d78e10eeca968 100644 --- a/packages/web-components/src/color/neutral-fill-card.spec.ts +++ b/packages/web-components/src/color/neutral-fill-card.spec.ts @@ -1,6 +1,11 @@ -import { expect } from 'chai'; +import { parseColorHexRGB } from "@microsoft/fast-colors"; +import { expect } from "chai"; +import { PaletteRGB } from "../color-vNext/palette"; +import { SwatchRGB } from "../color-vNext/swatch"; import { DesignSystem, DesignSystemDefaults } from '../fluent-design-system'; -import { neutralFillCard } from './neutral-fill-card'; +import { neutralBaseColor } from "./color-constants"; +import { neutralFillCard } from "./neutral-fill-card"; +import { neutralFillCard as neutralFillCardNew } from "../color-vNext/recipes/neutral-fill-card" describe('neutralFillCard', (): void => { it('should operate on design system defaults', (): void => { @@ -40,3 +45,15 @@ describe('neutralFillCard', (): void => { ); }); }); +describe("ensure parity between old and new recipe implementation", () => { + const color = (parseColorHexRGB(neutralBaseColor)!) + const palette = PaletteRGB.create(SwatchRGB.create(color.r, color.g, color.b)); + const { neutralFillCardDelta } = DesignSystemDefaults; + palette.swatches.forEach(( newSwatch, index ) => { + it(`should be the same for ${newSwatch}`, () => { + expect( + neutralFillCard({...DesignSystemDefaults, backgroundColor: DesignSystemDefaults.neutralPalette[index]}) + ).to.be.equal(neutralFillCardNew( palette, newSwatch, neutralFillCardDelta).toColorString().toUpperCase()) + }); + }) +}) diff --git a/packages/web-components/src/color/neutral-fill-input.spec.ts b/packages/web-components/src/color/neutral-fill-input.spec.ts index a6b2a4f135536..74ab26b139773 100644 --- a/packages/web-components/src/color/neutral-fill-input.spec.ts +++ b/packages/web-components/src/color/neutral-fill-input.spec.ts @@ -1,10 +1,14 @@ -import { expect } from 'chai'; +import { parseColorHexRGB } from "@microsoft/fast-colors"; +import { expect } from "chai"; +import { PaletteRGB } from "../color-vNext/palette"; +import { SwatchRGB } from "../color-vNext/swatch"; import { accentPalette as getAccentPalette, DesignSystem, DesignSystemDefaults, neutralPalette as getNeutralPalette, } from '../fluent-design-system'; +import { neutralBaseColor } from "./color-constants"; import { clamp, FillSwatchFamily, Swatch } from './common'; import { neutralFillInput, @@ -15,6 +19,7 @@ import { neutralFillInputSelected, } from './neutral-fill-input'; import { isDarkMode, Palette } from './palette'; +import { neutralFillInput as neutralFillInputNew } from "../color-vNext/recipes/neutral-fill-input"; describe('neutralFillInput', (): void => { const neutralPalette: Palette = getNeutralPalette(DesignSystemDefaults); @@ -107,3 +112,31 @@ describe('neutralFillInput', (): void => { }); }); }); +describe("ensure parity between old and new recipe implementation", () => { + const color = (parseColorHexRGB(neutralBaseColor)!) + const palette = PaletteRGB.create(SwatchRGB.create(color.r, color.g, color.b)); + palette.swatches.forEach(( newSwatch, index ) => { + const { + neutralFillInputRestDelta, + neutralFillInputHoverDelta, + neutralFillInputActiveDelta, + neutralFillInputFocusDelta, + neutralFillInputSelectedDelta + } = DesignSystemDefaults; + const oldValues = neutralFillInput({...DesignSystemDefaults, backgroundColor: DesignSystemDefaults.neutralPalette[index]}); + const newValues = neutralFillInputNew( + palette, + newSwatch, + neutralFillInputRestDelta, + neutralFillInputHoverDelta, + neutralFillInputActiveDelta, + neutralFillInputFocusDelta, + neutralFillInputSelectedDelta + ); + it(`should be the same for ${newSwatch}`, () => { + for (let key in oldValues) { + expect(oldValues[key]).to.equal(newValues[key].toColorString().toUpperCase()) + } + }); + }) +}) diff --git a/packages/web-components/src/color/neutral-fill-stealth.spec.ts b/packages/web-components/src/color/neutral-fill-stealth.spec.ts index 7433a16ceff17..7cb6cd2ce5413 100644 --- a/packages/web-components/src/color/neutral-fill-stealth.spec.ts +++ b/packages/web-components/src/color/neutral-fill-stealth.spec.ts @@ -1,3 +1,4 @@ +import { parseColorHexRGB } from "@microsoft/fast-colors"; import { expect } from 'chai'; import { accentPalette as getAccentPalette, @@ -15,6 +16,10 @@ import { } from './neutral-fill-stealth'; import { Palette } from './palette'; import { FillSwatchFamily, Swatch } from './common'; +import { PaletteRGB } from "../color-vNext/palette"; +import { SwatchRGB } from "../color-vNext/swatch"; +import { neutralBaseColor } from "./color-constants"; +import { neutralFillStealth as neutralFillStealthNew } from "../color-vNext/recipes/neutral-fill-stealth"; describe('neutralFillStealth', (): void => { const neutralPalette: Palette = getNeutralPalette(DesignSystemDefaults); @@ -107,3 +112,39 @@ describe('neutralFillStealth', (): void => { }); }); }); +describe("ensure parity between old and new recipe implementation", () => { + const color = (parseColorHexRGB(neutralBaseColor)!) + const palette = PaletteRGB.create(SwatchRGB.create(color.r, color.g, color.b)); + palette.swatches.forEach(( newSwatch, index ) => { + const { + neutralFillStealthRestDelta, + neutralFillStealthHoverDelta, + neutralFillStealthActiveDelta, + neutralFillStealthFocusDelta, + neutralFillStealthSelectedDelta, + neutralFillRestDelta, + neutralFillHoverDelta, + neutralFillActiveDelta, + neutralFillFocusDelta + } = DesignSystemDefaults; + const oldValues = neutralFillStealth({...DesignSystemDefaults, backgroundColor: DesignSystemDefaults.neutralPalette[index]}); + const newValues = neutralFillStealthNew( + palette, + newSwatch, + neutralFillStealthRestDelta, + neutralFillStealthHoverDelta, + neutralFillStealthActiveDelta, + neutralFillStealthFocusDelta, + neutralFillStealthSelectedDelta, + neutralFillRestDelta, + neutralFillHoverDelta, + neutralFillActiveDelta, + neutralFillFocusDelta + ); + it(`should be the same for ${newSwatch}`, () => { + for (let key in oldValues) { + expect(oldValues[key]).to.equal(newValues[key].toColorString().toUpperCase()) + } + }); + }) +}) diff --git a/packages/web-components/src/color/neutral-fill-toggle.spec.ts b/packages/web-components/src/color/neutral-fill-toggle.spec.ts new file mode 100644 index 0000000000000..1c750cf6ed57f --- /dev/null +++ b/packages/web-components/src/color/neutral-fill-toggle.spec.ts @@ -0,0 +1,30 @@ +import { parseColorHexRGB } from "@microsoft/fast-colors"; +import { expect } from "chai"; +import { PaletteRGB } from "../color-vNext/palette"; +import { neutralFillToggle as neutralFillToggleNew } from "../color-vNext/recipes/neutral-fill-toggle"; +import { SwatchRGB } from "../color-vNext/swatch"; +import { DesignSystemDefaults } from "../fluent-design-system"; +import { neutralBaseColor } from "./color-constants"; +import { neutralFillToggle } from "./neutral-fill-toggle"; + +describe("ensure parity between old and new recipe implementation", () => { + const color = (parseColorHexRGB(neutralBaseColor)!) + const palette = PaletteRGB.create(SwatchRGB.create(color.r, color.g, color.b)); + palette.swatches.forEach(( newSwatch, index ) => { + const { neutralFillToggleHoverDelta, neutralFillToggleActiveDelta, neutralFillToggleFocusDelta} = DesignSystemDefaults; + const oldValues = neutralFillToggle({...DesignSystemDefaults, backgroundColor: DesignSystemDefaults.neutralPalette[index]}); + const newValues = neutralFillToggleNew( + palette, + newSwatch, + 0, + neutralFillToggleHoverDelta, + neutralFillToggleActiveDelta, + neutralFillToggleFocusDelta, + ); + it(`should be the same for ${newSwatch}`, () => { + for (let key in oldValues) { + expect(oldValues[key]).to.equal(newValues[key].toColorString().toUpperCase()) + } + }); + }) +}) diff --git a/packages/web-components/src/color/neutral-fill.spec.ts b/packages/web-components/src/color/neutral-fill.spec.ts index dd26ede0b3b89..15024e73fe40f 100644 --- a/packages/web-components/src/color/neutral-fill.spec.ts +++ b/packages/web-components/src/color/neutral-fill.spec.ts @@ -1,3 +1,4 @@ +import { parseColorHexRGB } from "@microsoft/fast-colors"; import { expect } from 'chai'; import { accentPalette as getAccentPalette, @@ -15,6 +16,10 @@ import { } from './neutral-fill'; import { Palette } from './palette'; import { FillSwatchFamily, Swatch } from './common'; +import { neutralBaseColor } from "./color-constants"; +import { SwatchRGB } from "../color-vNext/swatch"; +import { PaletteRGB } from "../color-vNext/palette"; +import { neutralFill as neutralFillNew } from "../color-vNext/recipes/neutral-fill" describe('neutralFill', (): void => { const neutralPalette: Palette = getNeutralPalette(DesignSystemDefaults); @@ -102,3 +107,17 @@ describe('neutralFill', (): void => { }); }); }); +describe("ensure parity between old and new recipe implementation", () => { + const color = (parseColorHexRGB(neutralBaseColor)!) + const palette = PaletteRGB.create(SwatchRGB.create(color.r, color.g, color.b)); + palette.swatches.forEach(( newSwatch, index ) => { + const { neutralFillRestDelta, neutralFillHoverDelta, neutralFillActiveDelta, neutralFillFocusDelta, neutralFillSelectedDelta } = DesignSystemDefaults; + const oldValues = neutralFill({...DesignSystemDefaults, backgroundColor: DesignSystemDefaults.neutralPalette[index]}); + const newValues = neutralFillNew(palette, newSwatch, neutralFillRestDelta, neutralFillHoverDelta, neutralFillActiveDelta, neutralFillFocusDelta, neutralFillSelectedDelta ); + it(`should be the same for ${newSwatch}`, () => { + for (let key in oldValues) { + expect(oldValues[key]).to.equal(newValues[key].toColorString().toUpperCase()) + } + }); + }) +}) diff --git a/packages/web-components/src/color/neutral-focus.spec.ts b/packages/web-components/src/color/neutral-focus.spec.ts index 2335caa3ad840..73edb558572c6 100644 --- a/packages/web-components/src/color/neutral-focus.spec.ts +++ b/packages/web-components/src/color/neutral-focus.spec.ts @@ -1,7 +1,12 @@ +import { parseColorHexRGB } from "@microsoft/fast-colors"; import { expect } from 'chai'; import { DesignSystem, DesignSystemDefaults } from '../fluent-design-system'; import { neutralFocus } from './neutral-focus'; import { contrast } from './common'; +import { neutralBaseColor } from "./color-constants"; +import { PaletteRGB } from "../color-vNext/palette"; +import { SwatchRGB } from "../color-vNext/swatch"; +import { neutralFocus as neutralFocusNew } from "../color-vNext/recipes/neutral-focus"; describe('neutralFocus', (): void => { it('should return a string when invoked with an object', (): void => { @@ -16,3 +21,12 @@ describe('neutralFocus', (): void => { expect(contrast(neutralFocus({} as DesignSystem), '#FFF')).to.be.gte(3.5); }); }); +describe("ensure parity between old and new recipe implementation", () => { + const color = (parseColorHexRGB(neutralBaseColor)!) + const palette = PaletteRGB.create(SwatchRGB.create(color.r, color.g, color.b)); + palette.swatches.forEach(( newSwatch, index ) => { + it(`should be the same for ${newSwatch}`, () => { + expect(neutralFocus({...DesignSystemDefaults, backgroundColor: DesignSystemDefaults.neutralPalette[index]})).to.be.equal(neutralFocusNew( palette, newSwatch).toColorString().toUpperCase()) + }); + }) +}) diff --git a/packages/web-components/src/color/neutral-foreground-hint.spec.ts b/packages/web-components/src/color/neutral-foreground-hint.spec.ts index a4cb11f2a4122..75d8bae6f569a 100644 --- a/packages/web-components/src/color/neutral-foreground-hint.spec.ts +++ b/packages/web-components/src/color/neutral-foreground-hint.spec.ts @@ -1,3 +1,4 @@ +import { parseColorHexRGB } from "@microsoft/fast-colors"; import { expect } from 'chai'; import { accentPalette as getAccentPalette, @@ -7,6 +8,11 @@ import { import { neutralForegroundHint, neutralForegroundHintLarge } from './neutral-foreground-hint'; import { Palette } from './palette'; import { contrast, Swatch, SwatchRecipe } from './common'; +import { neutralBaseColor } from "./color-constants"; +import { PaletteRGB } from "../color-vNext/palette"; +import { SwatchRGB } from "../color-vNext/swatch"; +import { neutralForegroundHint as neutralForegroundHintNew } from "../color-vNext/recipes/neutral-foreground-hint"; + describe('neutralForegroundHint', (): void => { const neutralPalette: Palette = getNeutralPalette(DesignSystemDefaults); const accentPalette: Palette = getAccentPalette(DesignSystemDefaults); @@ -57,3 +63,14 @@ describe('neutralForegroundHint', (): void => { }); }); }); +describe("ensure parity between old and new recipe implementation", () => { + const color = (parseColorHexRGB(neutralBaseColor)!) + const palette = PaletteRGB.create(SwatchRGB.create(color.r, color.g, color.b)); + palette.swatches.forEach(( newSwatch, index ) => { + it(`should be the same for ${newSwatch}`, () => { + expect(neutralForegroundHintNew(palette, newSwatch).toColorString().toUpperCase()).to.equal( + neutralForegroundHint({...DesignSystemDefaults, backgroundColor: DesignSystemDefaults.neutralPalette[index]}) + ) + }) + }) +}) diff --git a/packages/web-components/src/color/neutral-foreground.spec.ts b/packages/web-components/src/color/neutral-foreground.spec.ts index a4cf9f4965d05..84a37d11b359c 100644 --- a/packages/web-components/src/color/neutral-foreground.spec.ts +++ b/packages/web-components/src/color/neutral-foreground.spec.ts @@ -1,7 +1,13 @@ +import { parseColorHexRGB } from "@microsoft/fast-colors"; import { expect } from 'chai'; +import { PaletteRGB } from "../color-vNext/palette"; +import { neutralForeground } from "../color-vNext/recipes/neutral-foreground"; +import { SwatchRGB } from "../color-vNext/swatch"; import { DesignSystemDefaults } from '../fluent-design-system'; import { neutralForegroundActive, neutralForegroundHover, neutralForegroundRest } from './neutral-foreground'; -import { contrast } from './common'; +import { neutralBaseColor } from "./color-constants"; +import { contrast } from "./common"; + describe('neutralForeground', (): void => { it('should return a string when invoked with an object', (): void => { @@ -50,3 +56,12 @@ describe('neutralForeground', (): void => { ).to.be.gte(14); }); }); +describe("ensure parity between old and new recipe implementation", () => { + const color = (parseColorHexRGB(neutralBaseColor)!) + const palette = PaletteRGB.create(SwatchRGB.create(color.r, color.g, color.b)); + palette.swatches.forEach(( newSwatch, index ) => { + it(`should be the same for ${newSwatch}`, () => { + expect(neutralForegroundRest({...DesignSystemDefaults, backgroundColor: DesignSystemDefaults.neutralPalette[index]})).to.be.equal(neutralForeground( palette, newSwatch).toColorString().toUpperCase()) + }); + }) +}) diff --git a/packages/web-components/src/color/neutral-layer.spec.ts b/packages/web-components/src/color/neutral-layer.spec.ts index b617e7cffb3b1..74cdb9ce30afb 100644 --- a/packages/web-components/src/color/neutral-layer.spec.ts +++ b/packages/web-components/src/color/neutral-layer.spec.ts @@ -1,3 +1,4 @@ +import { parseColorHexRGB } from "@microsoft/fast-colors"; import { expect } from 'chai'; import { DesignSystem, DesignSystemDefaults } from '../fluent-design-system'; import { @@ -10,6 +11,15 @@ import { neutralLayerL4, StandardLuminance, } from './neutral-layer'; +import { + neutralLayerFloating as neutralLayerFloatingNew +} from '../color-vNext/recipes/neutral-layer-floating'; +import { + neutralLayerCard as neutralLayerCardNew +} from '../color-vNext/recipes/neutral-layer-card'; +import { neutralBaseColor } from "./color-constants"; +import { PaletteRGB } from "../color-vNext/palette"; +import { SwatchRGB } from "../color-vNext/swatch"; const lightModeDesignSystem: DesignSystem = Object.assign({}, DesignSystemDefaults, { baseLayerLuminance: StandardLuminance.LightMode, @@ -129,6 +139,13 @@ describe('neutralLayer', (): void => { expect(color).not.to.equal(neutralLayerFloating(DesignSystemDefaults)); expect(DesignSystemDefaults.neutralPalette.includes(color)).to.be.ok; }); + it("should have a new implementation that matches the old implementation", () => { + const color = (parseColorHexRGB(neutralBaseColor)!) + const palette = PaletteRGB.create(SwatchRGB.create(color.r, color.g, color.b)); + + expect(neutralLayerFloating(lightModeDesignSystem)).to.equal(neutralLayerFloatingNew(palette, StandardLuminance.LightMode, lightModeDesignSystem.neutralFillCardDelta).toColorString().toUpperCase()) + expect(neutralLayerFloating(darkModeDesignSystem)).to.equal(neutralLayerFloatingNew(palette, StandardLuminance.DarkMode, lightModeDesignSystem.neutralFillCardDelta).toColorString().toUpperCase()) + }) }); describe('neutralLayerCardContainer', (): void => { it('should return a color from the neutral palette', (): void => { @@ -151,5 +168,12 @@ describe('neutralLayer', (): void => { expect(color).not.to.equal(neutralLayerCard(DesignSystemDefaults)); expect(DesignSystemDefaults.neutralPalette.includes(color)).to.be.ok; }); + it("should have a new implementation that matches the old implementation", () => { + const color = (parseColorHexRGB(neutralBaseColor)!) + const palette = PaletteRGB.create(SwatchRGB.create(color.r, color.g, color.b)); + + expect(neutralLayerCard(lightModeDesignSystem)).to.equal(neutralLayerCardNew(palette, StandardLuminance.LightMode, lightModeDesignSystem.neutralFillCardDelta).toColorString().toUpperCase()) + expect(neutralLayerCard(darkModeDesignSystem)).to.equal(neutralLayerCardNew(palette, StandardLuminance.DarkMode, lightModeDesignSystem.neutralFillCardDelta).toColorString().toUpperCase()) + }) }); }); diff --git a/packages/web-components/src/color/neutral-outline.spec.ts b/packages/web-components/src/color/neutral-outline.spec.ts index 6f9810389b706..af942280a234c 100644 --- a/packages/web-components/src/color/neutral-outline.spec.ts +++ b/packages/web-components/src/color/neutral-outline.spec.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { isColorStringHexRGB } from '@microsoft/fast-colors'; +import { isColorStringHexRGB, parseColorHexRGB } from "@microsoft/fast-colors"; import { DesignSystem, DesignSystemDefaults, @@ -15,6 +15,10 @@ import { } from './neutral-outline'; import { Palette } from './palette'; import { Swatch, SwatchFamily } from './common'; +import { neutralBaseColor } from "./color-constants"; +import { PaletteRGB } from "../color-vNext/palette"; +import { SwatchRGB } from "../color-vNext/swatch"; +import { neutralOutline as neutralOutlineNew } from "../color-vNext/recipes/neutral-outline" describe('neutralOutline', (): void => { const neutralPalette: Palette = getNeutralPalette(DesignSystemDefaults); @@ -80,3 +84,24 @@ describe('neutralOutline', (): void => { }); }); }); +describe("ensure parity between old and new recipe implementation", () => { + const color = (parseColorHexRGB(neutralBaseColor)!) + const palette = PaletteRGB.create(SwatchRGB.create(color.r, color.g, color.b)); + palette.swatches.forEach(( newSwatch, index ) => { + const { neutralOutlineRestDelta, neutralOutlineHoverDelta, neutralOutlineFocusDelta, neutralOutlineActiveDelta } = DesignSystemDefaults; + const oldValues = neutralOutline({...DesignSystemDefaults, backgroundColor: DesignSystemDefaults.neutralPalette[index]}); + const newValues = neutralOutlineNew( + palette, + newSwatch, + neutralOutlineRestDelta, + neutralOutlineHoverDelta, + neutralOutlineActiveDelta, + neutralOutlineFocusDelta, + ); + it(`should be the same for ${newSwatch}`, () => { + for (let key in oldValues) { + expect(oldValues[key]).to.equal(newValues[key].toColorString().toUpperCase()) + } + }); + }) +}) diff --git a/packages/web-components/src/default-palette.ts b/packages/web-components/src/default-palette.ts index 54abd438df7f0..ba340464c54f6 100644 --- a/packages/web-components/src/default-palette.ts +++ b/packages/web-components/src/default-palette.ts @@ -3,194 +3,194 @@ * This file generated by web-components/build/generate-default-palettes.js */ export const neutralPalette: string[] = [ - '#FFFFFF', - '#FCFCFC', - '#FAFAFA', - '#F7F7F7', - '#F5F5F5', - '#F2F2F2', - '#EFEFEF', - '#EDEDED', - '#EAEAEA', - '#E8E8E8', - '#E5E5E5', - '#E2E2E2', - '#E0E0E0', - '#DDDDDD', - '#DBDBDB', - '#D8D8D8', - '#D6D6D6', - '#D3D3D3', - '#D0D0D0', - '#CECECE', - '#CBCBCB', - '#C9C9C9', - '#C6C6C6', - '#C3C3C3', - '#C1C1C1', - '#BEBEBE', - '#BCBCBC', - '#B9B9B9', - '#B6B6B6', - '#B4B4B4', - '#B1B1B1', - '#AFAFAF', - '#ACACAC', - '#A9A9A9', - '#A7A7A7', - '#A4A4A4', - '#A2A2A2', - '#9F9F9F', - '#9D9D9D', - '#9A9A9A', - '#979797', - '#959595', - '#929292', - '#909090', - '#8D8D8D', - '#8A8A8A', - '#888888', - '#858585', - '#838383', - '#808080', - '#7D7D7D', - '#7B7B7B', - '#787878', - '#767676', - '#737373', - '#717171', - '#6E6E6E', - '#6B6B6B', - '#696969', - '#666666', - '#646464', - '#616161', - '#5F5F5F', - '#5C5C5C', - '#5A5A5A', - '#575757', - '#545454', - '#525252', - '#4F4F4F', - '#4D4D4D', - '#4A4A4A', - '#484848', - '#454545', - '#424242', - '#404040', - '#3D3D3D', - '#3B3B3B', - '#383838', - '#363636', - '#333333', - '#313131', - '#2E2E2E', - '#2B2B2B', - '#292929', - '#262626', - '#242424', - '#212121', - '#1E1E1E', - '#1B1B1B', - '#181818', - '#151515', - '#121212', - '#101010', - '#000000', + "#FFFFFF", + "#FCFCFC", + "#FAFAFA", + "#F7F7F7", + "#F5F5F5", + "#F2F2F2", + "#EFEFEF", + "#EDEDED", + "#EAEAEA", + "#E8E8E8", + "#E5E5E5", + "#E2E2E2", + "#E0E0E0", + "#DDDDDD", + "#DBDBDB", + "#D8D8D8", + "#D6D6D6", + "#D3D3D3", + "#D0D0D0", + "#CECECE", + "#CBCBCB", + "#C9C9C9", + "#C6C6C6", + "#C3C3C3", + "#C1C1C1", + "#BEBEBE", + "#BCBCBC", + "#B9B9B9", + "#B6B6B6", + "#B4B4B4", + "#B1B1B1", + "#AFAFAF", + "#ACACAC", + "#A9A9A9", + "#A7A7A7", + "#A4A4A4", + "#A2A2A2", + "#9F9F9F", + "#9D9D9D", + "#9A9A9A", + "#979797", + "#959595", + "#929292", + "#909090", + "#8D8D8D", + "#8A8A8A", + "#888888", + "#858585", + "#838383", + "#808080", + "#7D7D7D", + "#7B7B7B", + "#787878", + "#767676", + "#737373", + "#717171", + "#6E6E6E", + "#6B6B6B", + "#696969", + "#666666", + "#646464", + "#616161", + "#5F5F5F", + "#5C5C5C", + "#5A5A5A", + "#575757", + "#545454", + "#525252", + "#4F4F4F", + "#4D4D4D", + "#4A4A4A", + "#484848", + "#454545", + "#424242", + "#404040", + "#3D3D3D", + "#3B3B3B", + "#383838", + "#363636", + "#333333", + "#313131", + "#2E2E2E", + "#2B2B2B", + "#292929", + "#262626", + "#242424", + "#212121", + "#1E1E1E", + "#1B1B1B", + "#181818", + "#151515", + "#121212", + "#101010", + "#000000" ]; export const accentPalette: string[] = [ - '#FFFFFF', - '#FBFDFE', - '#F6FAFE', - '#F2F8FD', - '#EEF6FC', - '#E9F4FB', - '#E5F1FB', - '#E1EFFA', - '#DCEDF9', - '#D8EAF8', - '#D4E8F8', - '#CFE6F7', - '#CBE4F6', - '#C7E1F6', - '#C2DFF5', - '#BEDDF4', - '#BADAF3', - '#B6D8F3', - '#B1D6F2', - '#ADD4F1', - '#A9D1F0', - '#A4CFF0', - '#A0CDEF', - '#9CCAEE', - '#97C8EE', - '#93C6ED', - '#8FC4EC', - '#8AC1EB', - '#86BFEB', - '#82BDEA', - '#7DBAE9', - '#79B8E8', - '#75B6E8', - '#70B3E7', - '#6CB1E6', - '#68AFE5', - '#63ADE5', - '#5FAAE4', - '#5BA8E3', - '#56A6E3', - '#52A3E2', - '#4EA1E1', - '#499FE0', - '#459DE0', - '#419ADF', - '#3D98DE', - '#3896DD', - '#3493DD', - '#3091DC', - '#2B8FDB', - '#278DDB', - '#238ADA', - '#1E88D9', - '#1A86D8', - '#1683D8', - '#1181D7', - '#0D7FD6', - '#097DD5', - '#047AD5', - '#0078D4', - '#0075CF', - '#0072C9', - '#006FC4', - '#006CBE', - '#0069B9', - '#0066B4', - '#0063AE', - '#0060A9', - '#005CA3', - '#00599E', - '#005699', - '#005393', - '#00508E', - '#004D88', - '#004A83', - '#00477D', - '#004478', - '#004173', - '#003E6D', - '#003B68', - '#003862', - '#00355D', - '#003258', - '#002F52', - '#002B4D', - '#002847', - '#002542', - '#00223C', - '#001F36', - '#001B30', - '#00182B', - '#001525', - '#00121F', - '#000000', + "#FFFFFF", + "#FBFDFE", + "#F6FAFE", + "#F2F8FD", + "#EEF6FC", + "#E9F4FB", + "#E5F1FB", + "#E1EFFA", + "#DCEDF9", + "#D8EAF8", + "#D4E8F8", + "#CFE6F7", + "#CBE4F6", + "#C7E1F6", + "#C2DFF5", + "#BEDDF4", + "#BADAF3", + "#B6D8F3", + "#B1D6F2", + "#ADD4F1", + "#A9D1F0", + "#A4CFF0", + "#A0CDEF", + "#9CCAEE", + "#97C8EE", + "#93C6ED", + "#8FC4EC", + "#8AC1EB", + "#86BFEB", + "#82BDEA", + "#7DBAE9", + "#79B8E8", + "#75B6E8", + "#70B3E7", + "#6CB1E6", + "#68AFE5", + "#63ADE5", + "#5FAAE4", + "#5BA8E3", + "#56A6E3", + "#52A3E2", + "#4EA1E1", + "#499FE0", + "#459DE0", + "#419ADF", + "#3D98DE", + "#3896DD", + "#3493DD", + "#3091DC", + "#2B8FDB", + "#278DDB", + "#238ADA", + "#1E88D9", + "#1A86D8", + "#1683D8", + "#1181D7", + "#0D7FD6", + "#097DD5", + "#047AD5", + "#0078D4", + "#0075CF", + "#0072C9", + "#006FC4", + "#006CBE", + "#0069B9", + "#0066B4", + "#0063AE", + "#0060A9", + "#005CA3", + "#00599E", + "#005699", + "#005393", + "#00508E", + "#004D88", + "#004A83", + "#00477D", + "#004478", + "#004173", + "#003E6D", + "#003B68", + "#003862", + "#00355D", + "#003258", + "#002F52", + "#002B4D", + "#002847", + "#002542", + "#00223C", + "#001F36", + "#001B30", + "#00182B", + "#001525", + "#00121F", + "#000000" ];