Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/upset-ties-spend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@adaptive-web/adaptive-ui": patch
---

AUI: Updated layer fill interactive to invert colors to avoid white on white scenario
5 changes: 5 additions & 0 deletions packages/adaptive-ui-explorer/src/components/color-block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
highlightFillSubtleInverseControlStyles,
highlightForegroundReadableControlStyles,
highlightOutlineDiscernibleControlStyles,
layerFillInteractiveControlStyles,
neutralDividerDiscernibleElementStyles,
neutralDividerSubtleElementStyles,
neutralFillDiscernibleControlStyles,
Expand Down Expand Up @@ -82,6 +83,10 @@ const backplateComponents = html<ColorBlock>`
Accent subtle inverse
</app-style-example>

<app-style-example :disabledState=${x => x.disabledState} :showSwatches=${x => x.showSwatches} :styles="${x => layerFillInteractiveControlStyles}">
Layer interactive
</app-style-example>

<app-style-example :disabledState=${x => x.disabledState} :showSwatches=${x => x.showSwatches} :styles="${x => neutralFillIdealControlStyles}">
Neutral ideal
</app-style-example>
Expand Down
3 changes: 3 additions & 0 deletions packages/adaptive-ui/docs/api-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,9 @@ export type InteractivityDefinition = {
interactive?: string;
};

// @public
export function invertingPaletteDeltasForSet(palette: Palette, reference: RelativeLuminance, restDelta: number, hoverDelta: number, activeDelta: number, focusDelta: number, disabledDelta: number): InteractiveValues<number>;

// @public
export function isDark(color: RelativeLuminance): boolean;

Expand Down
1 change: 1 addition & 0 deletions packages/adaptive-ui/src/core/color/recipes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from "./contrast-and-delta-swatch-set.js";
export * from "./contrast-swatch.js";
export * from "./delta-swatch-set.js";
export * from "./delta-swatch.js";
export * from "./inverting-palette-deltas-for-set.js";
export * from "./hue-shift-gradient.js";
export * from "./ideal-color-delta-swatch-set.js";
export * from "./two-palette-gradient.js";
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { InteractiveValues } from "../../types.js";
import { Palette } from "../palette.js";
import { RelativeLuminance } from "../utilities/relative-luminance.js";

/**
* Checks if the supplied delta set for palette access is valid, or flips sign if not to keep deltas in bounds.
*
* Returns a new set of deltas so the indices for all states (rest, hover, active, focus) are in bounds.
* Flipping is always based on the rest delta; the other deltas are adjusted to preserve their *relative* difference to rest.
*
* @param palette - The Palette used to find the Colors
* @param reference - The reference color
* @param restDelta - The rest state offset from `reference`
* @param hoverDelta - The hover state offset from `reference`
* @param activeDelta - The active state offset from `reference`
* @param focusDelta - The focus state offset from `reference`
* @param disabledDelta - The disabled state offset from `reference`
* @returns An interactive set of deltas with possibly flipped and shifted values
*
* @public
*/
export function invertingPaletteDeltasForSet(palette: Palette, reference: RelativeLuminance, restDelta: number, hoverDelta: number, activeDelta: number, focusDelta: number, disabledDelta: number): InteractiveValues<number> {
const referenceIndex = palette.closestIndexOf(reference);
const deltas = [restDelta, hoverDelta, activeDelta, focusDelta];

// Compute the indices they'll hit
const indices = deltas.map(d => referenceIndex + d);

// Check if all indices are within palette bounds
const withinBounds = indices.every(idx => idx >= 0 && idx < palette.swatches.length);

if (withinBounds) {
// All are in bounds as-is
return {
rest: restDelta,
hover: hoverDelta,
active: activeDelta,
focus: focusDelta,
disabled: disabledDelta,
};
}

// The offset of other deltas from restDelta
const hoverOffset = hoverDelta - restDelta;
const activeOffset = activeDelta - restDelta;
const focusOffset = focusDelta - restDelta;
const disabledOffset = disabledDelta - restDelta;

const flippedRestDelta = restDelta * -1;

return {
rest: flippedRestDelta,
hover: flippedRestDelta + hoverOffset,
active: flippedRestDelta + activeOffset,
focus: flippedRestDelta + focusOffset,
disabled: flippedRestDelta + disabledOffset,
};
}
33 changes: 21 additions & 12 deletions packages/adaptive-ui/src/reference/layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { DesignTokenResolver } from "@microsoft/fast-foundation";
import { DesignTokenType } from "../core/adaptive-design-tokens.js";
import { Palette, PaletteDirectionValue } from "../core/color/palette.js";
import { ColorRecipeParams, InteractivePaintSet } from "../core/color/recipe.js";
import { deltaSwatch, deltaSwatchSet } from "../core/color/recipes/index.js";
import { deltaSwatch, deltaSwatchSet, invertingPaletteDeltasForSet } from "../core/color/recipes/index.js";
import { Color } from "../core/color/color.js";
import { luminanceSwatch } from "../core/color/utilities/luminance-swatch.js";
import { StyleProperty } from "../core/modules/types.js";
Expand Down Expand Up @@ -50,7 +50,7 @@ export const layerFillRestDelta = createTokenDelta(layerFillName, "layer", -2);

/**
* @public
* @deprecated Use `layerFillLayerDelta` instead.
* @deprecated Use `layerFillRestDelta` instead.
*/
export const layerFillDelta = layerFillRestDelta;

Expand Down Expand Up @@ -246,18 +246,27 @@ export const layerFillDisabledDelta = createTokenDelta(layerFillInteractiveName,
* @public
*/
export const layerFillInteractiveRecipe = createTokenColorRecipe<InteractivePaintSet>(layerFillInteractiveName, StyleProperty.backgroundFill,
(resolve: DesignTokenResolver, params?: ColorRecipeParams): InteractivePaintSet =>
deltaSwatchSet(
resolve(layerPalette),
params?.reference || resolve(colorContext),
resolve(layerFillRestDelta),
resolve(layerFillHoverDelta),
resolve(layerFillActiveDelta),
resolve(layerFillFocusDelta),
resolve(layerFillDisabledDelta),
(resolve: DesignTokenResolver, params?: ColorRecipeParams): InteractivePaintSet => {
const palette = resolve(layerPalette);
const reference = params?.reference || resolve(colorContext);
const restDelta = resolve(layerFillRestDelta);
const hoverDelta = resolve(layerFillHoverDelta);
const activeDelta = resolve(layerFillActiveDelta);
const focusDelta = resolve(layerFillFocusDelta);
const disabledDelta = resolve(layerFillDisabledDelta);
const deltas = invertingPaletteDeltasForSet(palette, reference, restDelta, hoverDelta, activeDelta, focusDelta, disabledDelta);
return deltaSwatchSet(
palette,
reference,
deltas.rest,
deltas.hover,
deltas.active,
deltas.focus,
deltas.disabled,
undefined,
PaletteDirectionValue.darker,
),
);
}
);

export const layerFillInteractive = createTokenColorSet(layerFillInteractiveRecipe);
Expand Down
20 changes: 19 additions & 1 deletion packages/adaptive-ui/src/reference/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ import {
} from "./color.js";
import { densityControl, densityControlList, densityItemContainer, densityLayer, densityText } from "./density.js";
import { elevationCardInteractive, elevationCardRest, elevationDialog, elevationFlyout, elevationTooltip } from "./elevation.js";
import { layerFillFixedPlus1 } from "./layer.js";
import { layerFillFixedPlus1, layerFillInteractive } from "./layer.js";
import {
fontFamily,
fontWeight,
Expand Down Expand Up @@ -1137,6 +1137,24 @@ export const criticalForegroundReadableControlStyles: Styles = Styles.fromProper
"color.critical-foreground-readable-control",
);

/**
* Convenience style module for a layer-filled control (interactive).
*
* By default, only the foreground color meets accessibility, useful for a button or similar:
* - layer interactive background
* - neutral strong foreground (a11y)
* - transparent border
*
* @public
*/
export const layerFillInteractiveControlStyles: Styles = Styles.fromProperties(
{
...Fill.backgroundAndForeground(layerFillInteractive, neutralStrokeStrongRecipe),
...densityBorderStyles(transparent),
},
"color.layer-fill-interactive-control",
);

/**
* Convenience style module for a neutral-filled stealth control (interactive).
*
Expand Down
Loading