Shadis/src/_DesignSystem/Button/ButtonStyle.ts

190 lines
5.3 KiB
TypeScript
Executable File

import {
neutralForegroundHover,
neutralForegroundActive,
} from "@microsoft/fast-components-styles-msft";
/**
* Code taken from "@microsoft/fast-components-styles-msft"
* Modified to fit the custom design
*/
import { ButtonClassNameContract } from "@microsoft/fast-components-class-name-contracts-msft";
import { ComponentStyles, CSSRules } from "@microsoft/fast-jss-manager";
import {
directionSwitch,
format,
toPx,
applyFocusVisible,
} from "@microsoft/fast-jss-utilities";
import {
DesignSystem,
accentForegroundCut,
glyphSize,
horizontalSpacing,
focusOutlineWidth,
accentPalette,
backgroundColor,
accentFillHover,
applyPillCornerRadius,
neutralForegroundRest,
neutralFillHover,
highContrastSelected,
highContrastSelectedForeground,
highContrastOutlineFocus,
neutralFocus,
} from "@microsoft/fast-components-styles-msft";
import { ColorRGBA64, rgbToRelativeLuminance } from "@microsoft/fast-colors";
import { parseColorString } from "@microsoft/fast-components-styles-msft/dist/utilities/color/common";
import { getSwatch } from "@microsoft/fast-components-styles-msft/dist/utilities/color/palette";
import { ButtonStyles as MSFTStyle } from "@microsoft/fast-components-styles-msft";
import { mergeDesignSystem } from "@microsoft/fast-jss-manager-react";
/**
* This color should be unaffected by the changes
* between light/dark mode
*/
const applyAccentBackground: CSSRules<DesignSystem> = {
background: ds => getSwatch(45, accentPalette(ds)),
};
const shadowOpacityMultiple = (des: DesignSystem) =>
4 - 3 * rgbToRelativeLuminance(parseColorString(backgroundColor(des))); // white (1) = 1; black (0) = 2;
/**
* Applies shadows to the primary button which give it the pseudo-3d look-and-feel
* @param shadowSize Size of the bottom-border and drop-shadow
*/
const applyPrimaryShadow = (shadowSize: number): CSSRules<DesignSystem> => {
return {
"box-shadow": format(
"0 {0} 0 0 {1}, 0 {2} {3} 0 {4}",
() => toPx(shadowSize),
des => getSwatch(65, accentPalette(des)),
() => toPx(shadowSize + 2),
() => toPx(shadowSize * 1.4),
des => new ColorRGBA64(0, 0, 0, 0.35 * shadowOpacityMultiple(des)).toStringWebRGBA()
),
};
};
const applyBeforeMargin: CSSRules<DesignSystem> = {
"margin-right": directionSwitch(horizontalSpacing(4), ""),
"margin-left": directionSwitch("", horizontalSpacing(4)),
};
const styles: ComponentStyles<ButtonClassNameContract, DesignSystem> = {
button: {
padding: format("6px {0}", horizontalSpacing(focusOutlineWidth)),
background: "transparent",
},
button__primary: {
fill: accentForegroundCut,
color: accentForegroundCut,
...applyAccentBackground,
...applyPrimaryShadow(4),
"font-weight": "bold",
overflow: "visible",
position: "relative",
// More css-weighting, so that generic rules don't break the effect
"&$button": {
"margin-bottom": "4px",
},
// Additional spacing to avoid clipping
"&::before": {
transition: "inherit",
content: "''",
height: "2px",
width: "calc(100% + 4px)", // + 2px border left/right
display: "block",
position: "absolute",
left: "-2px",
top: "-2px", // + 2px border top
},
"&:hover:enabled": {
...applyAccentBackground,
...applyPrimaryShadow(2),
"margin-top": "2px",
"margin-bottom": "2px",
"text-decoration": "none",
"&::before": {
top: "-4px",
},
},
"&:active:enabled": {
...applyAccentBackground,
},
},
button_afterContent: {
"margin-right": directionSwitch("", horizontalSpacing(4)),
"margin-left": directionSwitch(horizontalSpacing(4), ""),
},
button_icon: {
display: "inline-block",
position: "relative",
width: glyphSize,
height: glyphSize,
"flex-shrink": "0",
},
button__hasIconAndContent: {
"& $button_beforeContent, & $button_icon": {
...applyBeforeMargin,
},
},
button__justified: {
"& span": {
position: "relative",
"&::before": {
content: "''",
position: "absolute",
backgroundColor: accentFillHover,
width: "100%",
height: "2px",
bottom: "-4px",
transform: "scaleX(0)",
transformOrigin: "right",
transition: "transform .3s",
},
},
"&:hover span::before": {
transform: "scaleX(1)",
transformOrigin: "left",
},
},
button__lightweight: {
background: "transparent",
color: neutralForegroundRest,
fill: neutralForegroundRest,
"&:hover:enabled, a&:not($button__disabled):hover": {
background: neutralFillHover,
color: neutralForegroundHover,
...highContrastSelected,
"& $button_beforeContent, & $button_afterContent": {
...highContrastSelectedForeground,
},
},
"&:active:enabled, a&:not($button__disabled):active": {
color: neutralForegroundActive,
background: "transparent",
},
...applyFocusVisible<DesignSystem>({
...highContrastOutlineFocus,
"border-color": neutralFocus,
}),
},
button__iconOnly: {
padding: "8px",
margin: "5px",
height: "auto",
...applyPillCornerRadius(),
"& $button_icon": {
width: "17px",
height: "17px",
},
},
};
export default mergeDesignSystem(MSFTStyle, styles);