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/neat-plants-spend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@adaptive-web/adaptive-ui": patch
---

Fixed an issue with overlay handling of transparent colors
4 changes: 4 additions & 0 deletions packages/adaptive-ui-explorer/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
layerFillFixedMinus2,
layerFillFixedMinus3,
layerFillFixedPlus1,
neutralAsOverlay,
neutralBaseColor,
neutralPalette,
wcagContrastLevel
Expand Down Expand Up @@ -243,6 +244,9 @@ export class App extends FASTElement {

app.highlightPalette = highlightPalette.getValueFor(app.canvas);
break;
case "neutralAsOverlay":
neutralAsOverlay.setValueFor(app.canvas, source.neutralAsOverlay);
break;
case "showOnlyLayerBackgrounds":
app.updateBackgrounds();
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ export function controlPaneTemplate<T extends ControlPane>(): ElementViewTemplat
/>
</div>

<adaptive-switch
id="neutralAsOverlay"
:checked=${twoWay((x) => x.state.neutralAsOverlay)}
>Neutral as overlay</adaptive-switch>

<adaptive-switch
id="showOnlyLayerBackgrounds"
:checked=${twoWay((x) => x.state.showOnlyLayerBackgrounds)}
Expand Down
4 changes: 4 additions & 0 deletions packages/adaptive-ui-explorer/src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const State = Context.create<State>("State");
export interface State {
componentType: ComponentType;
neutralColor: Color;
neutralAsOverlay: boolean;
accentColor: Color;
highlightColor: Color;
showOnlyLayerBackgrounds: boolean;
Expand All @@ -31,6 +32,9 @@ export class DefaultState implements State {
@observable
public neutralColor: Color = PLACEHOLDER_COLOR;

@observable
public neutralAsOverlay: boolean = false;

@observable
public accentColor: Color = PLACEHOLDER_COLOR;

Expand Down
2 changes: 2 additions & 0 deletions packages/adaptive-ui/docs/api-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,12 @@ export class Color extends PaintBase {
}): Color;
static fromRgb(r: number, g: number, b: number, alpha?: number): Color;
protected readonly _intendedColor?: Color;
static isTransparent(color: Color_2): boolean;
static parse(color: string): Color | undefined;
// @deprecated
toColorString: () => string;
toString(): string;
static transparent: Color;
static unsafeOpacity(color: Color, alpha: number): Color;
}

Expand Down
21 changes: 21 additions & 0 deletions packages/adaptive-ui/src/core/color/color.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { cssDirective } from "@microsoft/fast-element";
import { type Color as CuloriColor, formatHex, formatRgb, modeLrgb, modeRgb, parse, type Rgb, useMode, wcagLuminance } from "culori/fn";
import { parseTransparent } from "culori";
import { PaintBase } from "./paint.js";
import { calculateOverlayColor } from "./utilities/opacity.js";

useMode(modeRgb);
// For luminance
useMode(modeLrgb);

const _transparent: Rgb = parseTransparent("transparent");

/**
* Represents a color.
*
Expand Down Expand Up @@ -44,6 +47,9 @@ export class Color extends PaintBase {
* @returns The color value in string format
*/
public toString(): string {
if (Color.isTransparent(this.color)) {
return "transparent";
}
return this.color.alpha !== undefined && this.color.alpha < 1 ? formatRgb(this.color) : formatHex(this.color);
}

Expand Down Expand Up @@ -99,6 +105,21 @@ export class Color extends PaintBase {
}
}

/**
* A Color representing the full transparent.
*/
public static transparent: Color = new Color(_transparent);

/**
* Checks if a color is transparent.
*
* @param color - The color to check.
* @returns True if the color is transparent, false otherwise.
*/
public static isTransparent(color: CuloriColor): boolean {
return color.alpha === 0;
}

/**
* Creates a new Color as an overlay representation of the `intendedColor` over `reference`.
*
Expand Down
13 changes: 5 additions & 8 deletions packages/adaptive-ui/src/core/color/utilities/conditional.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { Color } from "../color.js";
import { InteractiveColorSet } from "../recipe.js";
import { _white } from "./color-constants.js";

const _transparentWhite = Color.unsafeOpacity(_white, 0);

/**
* Return an interactive set of the provided tokens or a no-op "transparent" set of tokens.
Expand All @@ -20,10 +17,10 @@ export function conditionalSwatchSet(
}

return {
rest: _transparentWhite,
hover: _transparentWhite,
active: _transparentWhite,
focus: _transparentWhite,
disabled: _transparentWhite,
rest: Color.transparent,
hover: Color.transparent,
active: Color.transparent,
focus: Color.transparent,
disabled: Color.transparent,
};
}
5 changes: 5 additions & 0 deletions packages/adaptive-ui/src/core/color/utilities/opacity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ function calcRgbOverlay(rgbMatch: Rgb, rgbBackground: Rgb, rgbOverlay: Rgb): num
*/
export function calculateOverlayColor(match: CuloriColor, background: CuloriColor): Rgb {
const rgbMatch = rgb(match);

if (Color.isTransparent(rgbMatch) || Color.isTransparent(background)) {
return rgbMatch;
}

const rgbBackground = rgb(background);
let overlay = rgbBlack;
let alpha = calcRgbOverlay(rgbMatch, rgbBackground, overlay);
Expand Down
Loading