Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ import { storiesOf } from "@storybook/react";
import React from "react";
import { OutlineButton } from "./";
import { glyphFactory, SVGGlyph } from "../../assets/svg-element";
import { DesignSystemProvider } from "@microsoft/fast-jss-manager-react";
import {
accentPalette,
DesignSystem,
DesignSystemDefaults,
offsetsAlgorithm,
} from "@microsoft/fast-components-styles-msft";
import { colorRecipeFactory } from "@microsoft/fast-components-styles-msft/dist/utilities/color/common";

storiesOf("Outline button", module)
.add("Default", () => <OutlineButton>Outline button</OutlineButton>)
Expand Down Expand Up @@ -48,4 +56,18 @@ storiesOf("Outline button", module)
>
Disabled outline anchor button
</OutlineButton>
));
))
.add("Alternate style", () => {
const alternateDesignSystem: DesignSystem = Object.assign(
{},
DesignSystemDefaults,
{
neutralOutline: offsetsAlgorithm(accentPalette, 10, 40, 16, 50),
}
);
return (
<DesignSystemProvider designSystem={alternateDesignSystem}>
<OutlineButton>Alternate style outline button</OutlineButton>
</DesignSystemProvider>
);
});
14 changes: 14 additions & 0 deletions packages/fast-components-styles-msft/src/design-system/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { FontWeight } from "../utilities/fonts";
import designSystemSchema from "./design-system.schema";
import { accentPalette, neutralPalette } from "../default-palette";
import { isFunction } from "lodash-es";
import { SwatchFamilyResolver } from "../utilities/color/common";
import { offsetsAlgorithm } from "../utilities/color/offsets-algorithm";

export const defaultFontWeights: FontWeight = {
light: 100,
Expand Down Expand Up @@ -202,6 +204,8 @@ export interface DesignSystem {
neutralOutlineHoverDelta: number;
neutralOutlineActiveDelta: number;
neutralOutlineFocusDelta: number;

neutralOutline: SwatchFamilyResolver;
}

const designSystemDefaults: DesignSystem = {
Expand Down Expand Up @@ -270,10 +274,20 @@ const designSystemDefaults: DesignSystem = {

neutralDividerRestDelta: 8,

/**
* These would be deprecated / removed
*/
neutralOutlineRestDelta: 25,
neutralOutlineHoverDelta: 40,
neutralOutlineActiveDelta: 16,
neutralOutlineFocusDelta: 25,

/**
* Recipes would be configured more like this. Is there a pattern we've used
* to define interface types like this in the schema instead of the properties
* that go into it?
*/
neutralOutline: offsetsAlgorithm(neutralPalette, 25, 40, 16, 25),
};

/**
Expand Down
21 changes: 10 additions & 11 deletions packages/fast-components-styles-msft/src/outline-button/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@ import { ComponentStyles } from "@microsoft/fast-jss-manager";
import { applyFocusVisible, format, subtract, toPx } from "@microsoft/fast-jss-utilities";
import { DesignSystem } from "../design-system";
import { baseButton, buttonStyles } from "../patterns/button";
import {
neutralFocus,
neutralForegroundRest,
neutralOutlineActive,
neutralOutlineHover,
neutralOutlineRest,
} from "../utilities/color";
import { neutralFocus, neutralForegroundRest } from "../utilities/color";
import { horizontalSpacing } from "../utilities/density";
import { focusOutlineWidth, outlineWidth } from "../utilities/design-system";
import {
focusOutlineWidth,
neutralOutlineRecipe,
outlineWidth,
} from "../utilities/design-system";
import {
highContrastDisabledBorder,
highContrastLinkBorder,
Expand All @@ -21,6 +19,7 @@ import {
highContrastSelected,
highContrastSelector,
} from "../utilities/high-contrast";
import { active, hover, rest } from "../utilities/color/common";

const styles: ComponentStyles<LightweightButtonClassNameContract, DesignSystem> = {
...baseButton,
Expand All @@ -32,15 +31,15 @@ const styles: ComponentStyles<LightweightButtonClassNameContract, DesignSystem>
border: format(
"{0} solid {1}",
toPx<DesignSystem>(outlineWidth),
neutralOutlineRest
rest(neutralOutlineRecipe)
),
padding: format("0 {0}", horizontalSpacing(outlineWidth)),
"&:hover:enabled": {
background: "transparent",
border: format(
"{0} solid {1}",
toPx<DesignSystem>(outlineWidth),
neutralOutlineHover
hover(neutralOutlineRecipe)
),
...highContrastSelected,
},
Expand All @@ -49,7 +48,7 @@ const styles: ComponentStyles<LightweightButtonClassNameContract, DesignSystem>
border: format(
"{0} solid {1}",
toPx<DesignSystem>(outlineWidth),
neutralOutlineActive
active(neutralOutlineRecipe)
),
},
...applyFocusVisible<DesignSystem>({
Expand Down
53 changes: 53 additions & 0 deletions packages/fast-components-styles-msft/src/utilities/color/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,59 @@ export function swatchFamilyToSwatchRecipeFactory<T extends SwatchFamily>(
};
}

function swatchFamilyResolverToSwatchRecipeFactory<T extends SwatchFamily>(
type: keyof T,
callback: DesignSystemResolver<SwatchFamilyResolver<T>>
): SwatchRecipe {
const memoizedRecipe: typeof callback = memoize(callback);
return (arg: DesignSystem | SwatchResolver): any => {
if (typeof arg === "function") {
return (designSystem: DesignSystem): Swatch => {
return memoizedRecipe(
Object.assign({}, designSystem, {
backgroundColor: arg(designSystem),
})
)(designSystem)[type as string];
};
} else {
return memoizedRecipe(arg)(arg)[type];
}
};
}

export function rest(
swatchFamilyResolver: DesignSystemResolver<SwatchFamilyResolver>
): SwatchRecipe {
return swatchFamilyResolverToSwatchRecipeFactory(
SwatchFamilyType.rest,
swatchFamilyResolver
);
}
export function hover(
swatchFamilyResolver: DesignSystemResolver<SwatchFamilyResolver>
): SwatchRecipe {
return swatchFamilyResolverToSwatchRecipeFactory(
SwatchFamilyType.hover,
swatchFamilyResolver
);
}
export function active(
swatchFamilyResolver: DesignSystemResolver<SwatchFamilyResolver>
): SwatchRecipe {
return swatchFamilyResolverToSwatchRecipeFactory(
SwatchFamilyType.active,
swatchFamilyResolver
);
}
export function focus(
swatchFamilyResolver: DesignSystemResolver<SwatchFamilyResolver>
): SwatchRecipe {
return swatchFamilyResolverToSwatchRecipeFactory(
SwatchFamilyType.focus,
swatchFamilyResolver
);
}

/**
* Converts a color string into a ColorRGBA64 instance.
* Supports #RRGGBB and rgb(r, g, b) formats
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,4 @@ export { neutralFocus, neutralFocusInnerAccent } from "./neutral-focus";
export { neutralPaletteConfig, accentPaletteConfig } from "./color-constants";
export { isDarkMode, palette, PaletteType, Palette } from "./palette";
export { createColorPalette } from "./create-color-palette";
export { offsetsAlgorithm } from "./offsets-algorithm";
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { DesignSystem } from "../../design-system";
import { findClosestBackgroundIndex, getSwatch, isDarkMode } from "./palette";
import {
ColorRecipe,
colorRecipeFactory,
Expand All @@ -9,54 +7,55 @@ import {
SwatchFamilyType,
SwatchRecipe,
} from "./common";
import { offsetsAlgorithm } from "./offsets-algorithm";
import { neutralPalette } from "../../default-palette";
import {
neutralOutlineActiveDelta,
neutralOutlineFocusDelta,
neutralOutlineHoverDelta,
neutralOutlineRestDelta,
neutralPalette,
} from "../design-system";

const neutralOutlineAlgorithm: SwatchFamilyResolver = (
designSystem: DesignSystem
): SwatchFamily => {
const palette: string[] = neutralPalette(designSystem);
const backgroundIndex: number = findClosestBackgroundIndex(designSystem);
const direction: 1 | -1 = isDarkMode(designSystem) ? -1 : 1;

const restDelta: number = neutralOutlineRestDelta(designSystem);
const restIndex: number = backgroundIndex + direction * restDelta;
const hoverDelta: number = neutralOutlineHoverDelta(designSystem);
const hoverIndex: number = restIndex + direction * (hoverDelta - restDelta);
const activeDelta: number = neutralOutlineActiveDelta(designSystem);
const activeIndex: number = restIndex + direction * (activeDelta - restDelta);
const focusDelta: number = neutralOutlineFocusDelta(designSystem);
const focusIndex: number = restIndex + direction * (focusDelta - restDelta);

return {
rest: getSwatch(restIndex, palette),
hover: getSwatch(hoverIndex, palette),
active: getSwatch(activeIndex, palette),
focus: getSwatch(focusIndex, palette),
};
};
const neutralOutlineAlgorithm: SwatchFamilyResolver = offsetsAlgorithm(
neutralPalette,
neutralOutlineRestDelta,
neutralOutlineHoverDelta,
neutralOutlineActiveDelta,
neutralOutlineFocusDelta
);

/**
* @deprecated
*/
export const neutralOutline: ColorRecipe<SwatchFamily> = colorRecipeFactory(
neutralOutlineAlgorithm
);

/**
* @deprecated
*/
export const neutralOutlineRest: SwatchRecipe = swatchFamilyToSwatchRecipeFactory(
SwatchFamilyType.rest,
neutralOutline
neutralOutlineAlgorithm
);
/**
* @deprecated
*/
export const neutralOutlineHover: SwatchRecipe = swatchFamilyToSwatchRecipeFactory(
SwatchFamilyType.hover,
neutralOutline
neutralOutlineAlgorithm
);
/**
* @deprecated
*/
export const neutralOutlineActive: SwatchRecipe = swatchFamilyToSwatchRecipeFactory(
SwatchFamilyType.active,
neutralOutline
neutralOutlineAlgorithm
);
/**
* @deprecated
*/
export const neutralOutlineFocus: SwatchRecipe = swatchFamilyToSwatchRecipeFactory(
SwatchFamilyType.focus,
neutralOutline
neutralOutlineAlgorithm
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {
checkDesignSystemResolver,
DesignSystem,
DesignSystemResolver,
} from "../../design-system";
import { SwatchFamily, SwatchFamilyResolver } from "./common";
import { findClosestBackgroundIndex, getSwatch, isDarkMode, Palette } from "./palette";

/**
* Function to derive colors from offset configuration.
*/
export function offsetsAlgorithm(
palette: Palette | DesignSystemResolver<Palette>,
restDelta: number | DesignSystemResolver<number>,
hoverDelta: number | DesignSystemResolver<number>,
activeDelta: number | DesignSystemResolver<number>,
focusDelta: number | DesignSystemResolver<number>
Copy link
Copy Markdown
Member

@chrisdholt chrisdholt Jan 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a thought, and I know this is in the idea stage - but perhaps this algo can be defined by more of a config driven aspect? That may help those using it and reading it. It seems like the algorithm needs two things: 1) Palette, 2) interactive state config for the deltas

export interface InteractiveOffsetConfig {
    restDelta: number | DesignSystemResolver<number>;
    hoverDelta: number | DesignSystemResolver<number>;
    activeDelta: number | DesignSystemResolver<number>;
    focusDelta: number | DesignSystemResolver<number>;
}

export function offsetsAlgorithm(palette: Palette | DesignSystemResolver<Palette>, config: InteractiveOffsetConfig): SwatchFamilyResolver { };

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the benefit here cleaner syntax, reuse of config, or both? It does seem like it would protect from potential future changes easier as properties could be added and defaulted or deprecated without changing the signature. I would probably put everything in though, including the palette. I could see an implementation of this that wants both an accent and neutral, or even success or error, etc.

Perhaps in the design system service model there would be a way for style modules like this to retrieve properties from the registry of sorts. For instance, a custom design system might add a fourth color, and through composition the style could find it. This feels like a fairly standard composition problem that possibly has a framework or structure already in place. I wonder if we could pull something in.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially I see it for cleaner syntax - from a fn() standpoint, once you hit more than 3 inputs it's going to become difficult to manage. Considering the deltas are all "related" and inform the palette I think the syntax makes more sense than having them all at the same level.

): SwatchFamilyResolver {
return (designSystem: DesignSystem): SwatchFamily => {
const resolvedPalette: Palette = checkDesignSystemResolver(palette, designSystem);

const backgroundIndex: number = findClosestBackgroundIndex(designSystem);

const direction: 1 | -1 = isDarkMode(designSystem) ? -1 : 1;

const resolvedRestDelta: number = checkDesignSystemResolver(
restDelta,
designSystem
);
const resolvedHoverDelta: number = checkDesignSystemResolver(
hoverDelta,
designSystem
);
const resolvedActiveDelta: number = checkDesignSystemResolver(
activeDelta,
designSystem
);
const resolvedFocusDelta: number = checkDesignSystemResolver(
focusDelta,
designSystem
);

return {
rest: getSwatch(
backgroundIndex + direction * resolvedRestDelta,
resolvedPalette
),
hover: getSwatch(
backgroundIndex + direction * resolvedHoverDelta,
resolvedPalette
),
active: getSwatch(
backgroundIndex + direction * resolvedActiveDelta,
resolvedPalette
),
focus: getSwatch(
backgroundIndex + direction * resolvedFocusDelta,
resolvedPalette
),
};
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import designSystemDefaults, {
import { Palette } from "../utilities/color/palette";
import { Direction } from "@microsoft/fast-web-utilities";
import { FontWeight } from "./fonts";
import { SwatchFamilyResolver } from "./color/common";

/**
* Safely retrieves the value from a key of the DesignSystem.
Expand Down Expand Up @@ -250,3 +251,7 @@ export const getFontWeight: DesignSystemResolver<FontWeight> = getDesignSystemVa
export const neutralOutlineFocusDelta: DesignSystemResolver<
number
> = getDesignSystemValue("neutralOutlineFocusDelta");

export const neutralOutlineRecipe: DesignSystemResolver<
SwatchFamilyResolver
> = getDesignSystemValue("neutralOutline");