diff --git a/package.json b/package.json
index 4be46dba03..0eb525c280 100644
--- a/package.json
+++ b/package.json
@@ -83,6 +83,7 @@
"babel-loader": "^8.2.3",
"babel-plugin-dynamic-import-node": "^2.3.3",
"chromatic": "^6.0.4",
+ "class-variance-authority": "^0.7.0",
"eslint": "^8.40.0",
"eslint-config-prettier": "^8.8.0",
"eslint-import-resolver-typescript": "^3.5.5",
diff --git a/packages/wonder-blocks-button/package.json b/packages/wonder-blocks-button/package.json
index d57734d1aa..9816817eaf 100644
--- a/packages/wonder-blocks-button/package.json
+++ b/packages/wonder-blocks-button/package.json
@@ -31,6 +31,7 @@
"react-router-dom": "5.3.0"
},
"devDependencies": {
- "@khanacademy/wb-dev-build-settings": "^1.0.0"
+ "@khanacademy/wb-dev-build-settings": "^1.0.0",
+ "class-variance-authority": "^0.7.0"
}
}
\ No newline at end of file
diff --git a/packages/wonder-blocks-button/src/components/button-core.tsx b/packages/wonder-blocks-button/src/components/button-core.tsx
index c02543fc9d..771a0b7e57 100644
--- a/packages/wonder-blocks-button/src/components/button-core.tsx
+++ b/packages/wonder-blocks-button/src/components/button-core.tsx
@@ -1,31 +1,472 @@
import * as React from "react";
-import {CSSProperties, StyleSheet} from "aphrodite";
+// import {CSSProperties, StyleSheet} from "aphrodite";
+import {cva} from "class-variance-authority";
import {Link} from "react-router-dom";
import {__RouterContext} from "react-router";
import {LabelLarge, LabelSmall} from "@khanacademy/wonder-blocks-typography";
-import {addStyle, View} from "@khanacademy/wonder-blocks-core";
+import {View} from "@khanacademy/wonder-blocks-core";
import {CircularSpinner} from "@khanacademy/wonder-blocks-progress-spinner";
import {isClientSideUrl} from "@khanacademy/wonder-blocks-clickable";
import {
- ThemedStylesFn,
+ // ThemedStylesFn,
useScopedTheme,
- useStyles,
+ // useStyles,
} from "@khanacademy/wonder-blocks-theming";
import type {
ChildrenProps,
ClickableState,
} from "@khanacademy/wonder-blocks-clickable";
+import sharedStyles from "./button.module.css";
import type {SharedProps} from "./button";
-import {ButtonThemeContext, ButtonThemeContract} from "../themes/themed-button";
+import {ButtonThemeContext} from "../themes/themed-button";
import {ButtonIcon} from "./button-icon";
type Props = SharedProps & ChildrenProps & ClickableState;
-const StyledAnchor = addStyle("a");
-const StyledButton = addStyle("button");
-const StyledLink = addStyle(Link);
+// const StyledAnchor = addStyle("a");
+// const StyledButton = addStyle("button");
+// const StyledLink = addStyle(Link);
+const StyledAnchor = "a";
+const StyledButton = "button";
+// const StyledLink = Link;
+
+const buttonStyles = cva(sharedStyles.default, {
+ variants: {
+ kind: {
+ primary: [sharedStyles.primary, sharedStyles.primaryDefault],
+ secondary: [sharedStyles.secondary, sharedStyles.secondaryDefault],
+ tertiary: [sharedStyles.tertiary, sharedStyles.tertiaryDefault],
+ },
+ size: {
+ small: sharedStyles.small,
+ medium: sharedStyles.medium,
+ large: sharedStyles.large,
+ },
+ color: {
+ default: {},
+ destructive: {},
+ },
+ light: {
+ true: {},
+ false: {},
+ },
+ state: {
+ default: {},
+ hover: {},
+ focus: {},
+ active: {},
+ },
+ disabled: {
+ true: sharedStyles.disabled,
+ false: {},
+ },
+ },
+ compoundVariants: [
+ // primary
+ {
+ kind: "primary",
+ color: "default",
+ light: false,
+ className: sharedStyles.primaryDefault,
+ },
+ {
+ kind: "primary",
+ color: "destructive",
+ light: false,
+ className: sharedStyles.primaryDestructive,
+ },
+ {
+ kind: "primary",
+ color: "default",
+ light: true,
+ className: sharedStyles.primaryDefaultLight,
+ },
+ {
+ kind: "primary",
+ color: "destructive",
+ light: true,
+ className: sharedStyles.primaryDestructiveLight,
+ },
+ {
+ kind: "primary",
+ size: "large",
+ className: sharedStyles.primarySizeLarge,
+ },
+ // Focus
+ {
+ kind: "primary",
+ color: "default",
+ light: false,
+ state: "focus",
+ className: sharedStyles.primaryDefaultFocus,
+ },
+ {
+ kind: "primary",
+ color: "default",
+ light: true,
+ state: "focus",
+ className: sharedStyles.primaryDefaultLightFocus,
+ },
+ {
+ kind: "primary",
+ color: "destructive",
+ light: false,
+ state: "focus",
+ className: sharedStyles.primaryDestructiveFocus,
+ },
+ {
+ kind: "primary",
+ color: "destructive",
+ light: true,
+ state: "focus",
+ className: sharedStyles.primaryDestructiveLightFocus,
+ },
+ // primary:active
+ {
+ kind: "primary",
+ color: "default",
+ light: false,
+ state: "active",
+ className: sharedStyles.primaryDefaultActive,
+ },
+ {
+ kind: "primary",
+ color: "default",
+ light: true,
+ state: "active",
+ className: sharedStyles.primaryDefaultLightActive,
+ },
+ {
+ kind: "primary",
+ color: "destructive",
+ light: false,
+ state: "active",
+ className: sharedStyles.primaryDestructiveActive,
+ },
+ {
+ kind: "primary",
+ color: "destructive",
+ light: true,
+ state: "active",
+ className: sharedStyles.primaryDestructiveLightActive,
+ },
+ // primary:disabled
+ {
+ kind: "primary",
+ color: "default",
+ light: false,
+ disabled: true,
+ className: sharedStyles.primaryDefaultDisabled,
+ },
+ {
+ kind: "primary",
+ color: "default",
+ light: true,
+ disabled: true,
+ className: sharedStyles.primaryDefaultLightDisabled,
+ },
+ {
+ kind: "primary",
+ color: "destructive",
+ light: false,
+ disabled: true,
+ className: sharedStyles.primaryDestructiveDisabled,
+ },
+ {
+ kind: "primary",
+ color: "destructive",
+ light: true,
+ disabled: true,
+ className: sharedStyles.primaryDestructiveLightDisabled,
+ },
+ // secondary
+ {
+ kind: "secondary",
+ color: "default",
+ light: false,
+ state: "default",
+ className: sharedStyles.secondaryDefault,
+ },
+ {
+ kind: "secondary",
+ color: "destructive",
+ light: false,
+ state: "default",
+ className: sharedStyles.secondaryDestructive,
+ },
+ {
+ kind: "secondary",
+ color: ["default", "destructive"],
+ light: true,
+ state: "default",
+ className: sharedStyles.secondaryLight,
+ },
+ // secondary:focus
+ {
+ kind: "secondary",
+ color: ["default", "destructive"],
+ light: false,
+ state: "focus",
+ className: sharedStyles.secondaryFocus,
+ },
+ {
+ kind: "secondary",
+ color: "default",
+ light: false,
+ state: "focus",
+ className: [
+ sharedStyles.secondaryDefault,
+ sharedStyles.secondaryDefaultFocus,
+ ],
+ },
+ {
+ kind: "secondary",
+ color: "destructive",
+ light: false,
+ state: "focus",
+ className: [
+ sharedStyles.secondaryDestructive,
+ sharedStyles.secondaryDestructiveFocus,
+ ],
+ },
+ {
+ kind: "secondary",
+ color: ["default", "destructive"],
+ light: true,
+ state: "focus",
+ className: sharedStyles.secondaryLightFocus,
+ },
+ // secondary:active
+ {
+ kind: "secondary",
+ color: ["default", "destructive"],
+ light: false,
+ state: "active",
+ className: sharedStyles.secondaryActive,
+ },
+ {
+ kind: "secondary",
+ color: "default",
+ light: false,
+ state: "active",
+ className: sharedStyles.secondaryDefaultActive,
+ },
+ {
+ kind: "secondary",
+ color: "destructive",
+ light: false,
+ state: "active",
+ className: sharedStyles.secondaryDestructiveActive,
+ },
+ {
+ kind: "secondary",
+ color: "default",
+ light: true,
+ state: "active",
+ className: sharedStyles.secondaryDefaultLightActive,
+ },
+ {
+ kind: "secondary",
+ color: "destructive",
+ light: true,
+ state: "active",
+ className: sharedStyles.secondaryDestructiveLightActive,
+ },
+ // secondary:disabled
+ {
+ kind: "secondary",
+ color: ["default", "destructive"],
+ light: false,
+ disabled: true,
+ className: sharedStyles.secondaryDisabled,
+ },
+ {
+ kind: "secondary",
+ color: "default",
+ light: true,
+ disabled: true,
+ className: sharedStyles.secondaryDefaultLightDisabled,
+ },
+ {
+ kind: "secondary",
+ color: "destructive",
+ light: true,
+ disabled: true,
+ className: sharedStyles.secondaryDestructiveLightDisabled,
+ },
+ {
+ kind: "secondary",
+ color: ["default", "destructive"],
+ light: true,
+ disabled: true,
+ className: sharedStyles.secondaryLightDisabled,
+ },
+ // tertiary
+ {
+ kind: "tertiary",
+ color: "default",
+ light: false,
+ state: "default",
+ className: sharedStyles.tertiaryDefault,
+ },
+ {
+ kind: "tertiary",
+ color: "destructive",
+ light: false,
+ state: "default",
+ className: sharedStyles.tertiaryDestructive,
+ },
+ {
+ kind: "tertiary",
+ color: ["default", "destructive"],
+ light: true,
+ state: "default",
+ className: sharedStyles.tertiaryLight,
+ },
+ // tertiary:hover
+ {
+ kind: "tertiary",
+ color: ["default", "destructive"],
+ light: false,
+ state: "hover",
+ className: sharedStyles.tertiaryHover,
+ },
+ {
+ kind: "tertiary",
+ color: "default",
+ light: false,
+ state: "hover",
+ className: sharedStyles.tertiaryDefaultHover,
+ },
+ {
+ kind: "tertiary",
+ color: "destructive",
+ light: false,
+ state: "hover",
+ className: sharedStyles.tertiaryDestructiveHover,
+ },
+ {
+ kind: "tertiary",
+ color: ["default", "destructive"],
+ light: true,
+ state: "hover",
+ className: sharedStyles.tertiaryLightHover,
+ },
+ // tertiary:focus
+ {
+ kind: "tertiary",
+ color: ["default", "destructive"],
+ light: false,
+ state: "focus",
+ className: sharedStyles.tertiaryFocus,
+ },
+ {
+ kind: "tertiary",
+ color: "default",
+ light: false,
+ state: "focus",
+ className: sharedStyles.tertiaryDefaultFocus,
+ },
+ {
+ kind: "tertiary",
+ color: "destructive",
+ light: false,
+ state: "focus",
+ className: sharedStyles.tertiaryDestructiveFocus,
+ },
+ {
+ kind: "tertiary",
+ color: ["default", "destructive"],
+ light: true,
+ state: "focus",
+ className: sharedStyles.tertiaryLightFocus,
+ },
+ // tertiary:active
+ {
+ kind: "tertiary",
+ color: ["default", "destructive"],
+ light: false,
+ state: "active",
+ className: sharedStyles.tertiaryActive,
+ },
+ {
+ kind: "tertiary",
+ color: "default",
+ light: false,
+ state: "active",
+ className: sharedStyles.tertiaryDefaultActive,
+ },
+ {
+ kind: "tertiary",
+ color: "destructive",
+ light: false,
+ state: "active",
+ className: sharedStyles.tertiaryDestructiveActive,
+ },
+ {
+ kind: "tertiary",
+ color: "default",
+ light: true,
+ state: "active",
+ className: sharedStyles.tertiaryDefaultLightActive,
+ },
+ {
+ kind: "tertiary",
+ color: "destructive",
+ light: true,
+ state: "active",
+ className: sharedStyles.tertiaryDestructiveLightActive,
+ },
+ // tertiary:disabled
+ {
+ kind: "tertiary",
+ color: ["default", "destructive"],
+ light: false,
+ disabled: true,
+ className: sharedStyles.tertiaryDisabled,
+ },
+ {
+ kind: "tertiary",
+ color: "default",
+ light: true,
+ disabled: true,
+ className: sharedStyles.tertiaryDefaultLightDisabled,
+ },
+ {
+ kind: "tertiary",
+ color: "destructive",
+ light: true,
+ disabled: true,
+ className: sharedStyles.tertiaryDestructiveLightDisabled,
+ },
+ {
+ kind: "tertiary",
+ color: ["default", "destructive"],
+ light: false,
+ disabled: true,
+ state: "focus",
+ className: sharedStyles.tertiaryDisabledFocus,
+ },
+ {
+ kind: "tertiary",
+ color: ["default", "destructive"],
+ light: true,
+ disabled: true,
+ state: "focus",
+ className: sharedStyles.tertiaryLightDisabledFocus,
+ },
+ ],
+ defaultVariants: {
+ kind: "primary",
+ size: "medium",
+ color: "default",
+ light: false,
+ state: "default",
+ disabled: false,
+ },
+});
const ButtonCore: React.ForwardRefExoticComponent<
Props &
@@ -34,8 +475,7 @@ const ButtonCore: React.ForwardRefExoticComponent<
typeof Link | HTMLButtonElement | HTMLAnchorElement,
Props
>(function ButtonCore(props: Props, ref) {
- const {theme, themeName} = useScopedTheme(ButtonThemeContext);
- const sharedStyles = useStyles(themedSharedStyles, theme);
+ const {theme} = useScopedTheme(ButtonThemeContext);
const renderInner = (router: any): React.ReactNode => {
const {
@@ -62,45 +502,60 @@ const ButtonCore: React.ForwardRefExoticComponent<
...restProps
} = props;
- const buttonStyles = _generateStyles(
- color,
- kind,
- light,
- size,
- theme,
- themeName,
- );
-
const disabled = spinner || disabledProp;
- const defaultStyle = [
- sharedStyles.shared,
- disabled && sharedStyles.disabled,
- startIcon && sharedStyles.withStartIcon,
- endIcon && sharedStyles.withEndIcon,
- buttonStyles.default,
- disabled && buttonStyles.disabled,
- // apply focus effect only to default and secondary buttons
- kind !== "tertiary" &&
- !disabled &&
- (pressed
- ? buttonStyles.active
- : (hovered || focused) && buttonStyles.focus),
- kind === "tertiary" &&
- !pressed &&
- focused && [
- buttonStyles.focus,
- disabled && buttonStyles.disabledFocus,
- ],
- size === "small" && sharedStyles.small,
- size === "large" && sharedStyles.large,
- ];
+ // const defaultStyle = [
+ // sharedStyles.shared,
+ // disabled && sharedStyles.disabled,
+ // startIcon && sharedStyles.withStartIcon,
+ // endIcon && sharedStyles.withEndIcon,
+ // buttonStyles.default,
+ // disabled && buttonStyles.disabled,
+ // // apply focus effect only to default and secondary buttons
+ // kind !== "tertiary" &&
+ // !disabled &&
+ // (pressed
+ // ? buttonStyles.active
+ // : (hovered || focused) && buttonStyles.focus),
+ // kind === "tertiary" &&
+ // !pressed &&
+ // focused && [
+ // buttonStyles.focus,
+ // disabled && buttonStyles.disabledFocus,
+ // ],
+ // size === "small" && sharedStyles.small,
+ // size === "large" && sharedStyles.large,
+ // ];
+
+ const buttonState = pressed
+ ? "active"
+ : hovered || focused
+ ? "focus"
+ : "default";
+
+ const defaultStyle = buttonStyles({
+ kind,
+ size,
+ color,
+ light,
+ state: buttonState,
+ disabled,
+ // extra styles
+ className: [
+ theme,
+ sharedStyles.shared,
+ startIcon && sharedStyles.withStartIcon,
+ endIcon && sharedStyles.withEndIcon,
+ style,
+ ],
+ });
const commonProps = {
"data-testid": testId,
id: id,
role: "button",
- style: [defaultStyle, style],
+ // style: [defaultStyle, style],
+ className: defaultStyle,
...restProps,
} as const;
@@ -115,11 +570,15 @@ const ButtonCore: React.ForwardRefExoticComponent<
spinner && sharedStyles.hiddenText,
kind === "tertiary" && sharedStyles.textWithFocus,
// apply press/hover effects on the label
+ // TODO(juan): figure out this part
kind === "tertiary" &&
!disabled &&
(pressed
- ? [buttonStyles.hover, buttonStyles.active]
- : hovered && buttonStyles.hover),
+ ? [
+ sharedStyles.tertiaryHover,
+ sharedStyles.tertiaryActive,
+ ]
+ : hovered && sharedStyles.tertiaryHover),
]}
testId={testId ? `${testId}-inner-label` : undefined}
>
@@ -174,7 +633,8 @@ const ButtonCore: React.ForwardRefExoticComponent<
testId ? `${testId}-end-icon-wrapper` : undefined
}
style={[
- styles.endIcon,
+ // styles.endIcon,
+ sharedStyles.endIcon,
sharedStyles.iconWrapper,
sharedStyles.endIconWrapper,
kind === "tertiary" &&
@@ -196,13 +656,13 @@ const ButtonCore: React.ForwardRefExoticComponent<
if (href && !disabled) {
return router && !skipClientNav && isClientSideUrl(href) ? (
- }
>
{contents}
-
+
) : (
= (theme) => ({
- shared: {
- position: "relative",
- display: "inline-flex",
- alignItems: "center",
- justifyContent: "center",
- height: theme.size.height.medium,
- paddingTop: 0,
- paddingBottom: 0,
- paddingLeft: theme.padding.large,
- paddingRight: theme.padding.large,
- border: "none",
- borderRadius: theme.border.radius.default,
- cursor: "pointer",
- outline: "none",
- textDecoration: "none",
- boxSizing: "border-box",
- // This removes the 300ms click delay on mobile browsers by indicating that
- // "double-tap to zoom" shouldn't be used on this element.
- touchAction: "manipulation",
- userSelect: "none",
- ":focus": {
- // Mobile: Removes a blue highlight style shown when the user clicks a button
- WebkitTapHighlightColor: "rgba(0,0,0,0)",
- },
- },
- disabled: {
- cursor: "auto",
- },
- small: {
- borderRadius: theme.border.radius.small,
- height: theme.size.height.small,
- },
- large: {
- borderRadius: theme.border.radius.large,
- height: theme.size.height.large,
- },
- text: {
- alignItems: "center",
- fontWeight: theme.font.weight.default,
- whiteSpace: "nowrap",
- overflow: "hidden",
- textOverflow: "ellipsis",
- display: "inline-block", // allows the button text to truncate
- pointerEvents: "none", // fix Safari bug where the browser was eating mouse events
- },
- largeText: {
- fontSize: theme.font.size.large,
- lineHeight: `${theme.font.lineHeight.large}px`,
- },
- textWithFocus: {
- position: "relative", // allows the tertiary button border to use the label width
- },
- hiddenText: {
- visibility: "hidden",
- },
- spinner: {
- position: "absolute",
- },
- startIcon: {
- marginRight: theme.padding.small,
- marginLeft: theme.margin.icon.offset,
- },
- tertiaryStartIcon: {
- // Undo the negative padding from startIcon since tertiary
- // buttons don't have extra padding.
- marginLeft: 0,
- },
- endIcon: {
- marginLeft: theme.padding.small,
- },
- iconWrapper: {
- borderRadius: theme.border.radius.icon,
- padding: theme.padding.xsmall,
- // View has a default minWidth of 0, which causes the label text
- // to encroach on the icon when it needs to truncate. We can fix
- // this by setting the minWidth to auto.
- minWidth: "auto",
- },
- iconWrapperSecondaryHovered: {
- backgroundColor: theme.color.bg.icon.secondaryHover,
- color: theme.color.text.icon.secondaryHover,
- },
- endIconWrapper: {
- marginLeft: theme.padding.small,
- marginRight: theme.margin.icon.offset,
- },
- endIconWrapperTertiary: {
- marginRight: 0,
- },
-});
+// const themedSharedStyles: ThemedStylesFn = (theme) => ({
+// shared: {
+// position: "relative",
+// display: "inline-flex",
+// alignItems: "center",
+// justifyContent: "center",
+// height: theme.size.height.medium,
+// paddingTop: 0,
+// paddingBottom: 0,
+// paddingLeft: theme.padding.large,
+// paddingRight: theme.padding.large,
+// border: "none",
+// borderRadius: theme.border.radius.default,
+// cursor: "pointer",
+// outline: "none",
+// textDecoration: "none",
+// boxSizing: "border-box",
+// // This removes the 300ms click delay on mobile browsers by indicating that
+// // "double-tap to zoom" shouldn't be used on this element.
+// touchAction: "manipulation",
+// userSelect: "none",
+// ":focus": {
+// // Mobile: Removes a blue highlight style shown when the user clicks a button
+// WebkitTapHighlightColor: "rgba(0,0,0,0)",
+// },
+// },
+// disabled: {
+// cursor: "auto",
+// },
+// small: {
+// borderRadius: theme.border.radius.small,
+// height: theme.size.height.small,
+// },
+// large: {
+// borderRadius: theme.border.radius.large,
+// height: theme.size.height.large,
+// },
+// text: {
+// alignItems: "center",
+// fontWeight: theme.font.weight.default,
+// whiteSpace: "nowrap",
+// overflow: "hidden",
+// textOverflow: "ellipsis",
+// display: "inline-block", // allows the button text to truncate
+// pointerEvents: "none", // fix Safari bug where the browser was eating mouse events
+// },
+// largeText: {
+// fontSize: theme.font.size.large,
+// lineHeight: `${theme.font.lineHeight.large}px`,
+// },
+// textWithFocus: {
+// position: "relative", // allows the tertiary button border to use the label width
+// },
+// hiddenText: {
+// visibility: "hidden",
+// },
+// spinner: {
+// position: "absolute",
+// },
+// startIcon: {
+// marginRight: theme.padding.small,
+// marginLeft: theme.margin.icon.offset,
+// },
+// tertiaryStartIcon: {
+// // Undo the negative padding from startIcon since tertiary
+// // buttons don't have extra padding.
+// marginLeft: 0,
+// },
+// endIcon: {
+// marginLeft: theme.padding.small,
+// },
+// iconWrapper: {
+// borderRadius: theme.border.radius.icon,
+// padding: theme.padding.xsmall,
+// // View has a default minWidth of 0, which causes the label text
+// // to encroach on the icon when it needs to truncate. We can fix
+// // this by setting the minWidth to auto.
+// minWidth: "auto",
+// },
+// iconWrapperSecondaryHovered: {
+// backgroundColor: theme.color.bg.icon.secondaryHover,
+// color: theme.color.text.icon.secondaryHover,
+// },
+// endIconWrapper: {
+// marginLeft: theme.padding.small,
+// marginRight: theme.margin.icon.offset,
+// },
+// endIconWrapperTertiary: {
+// marginRight: 0,
+// },
+// });
-const styles: Record = {};
+// const styles: Record = {};
-// export for testing only
-export const _generateStyles = (
- buttonColor = "default",
- kind: "primary" | "secondary" | "tertiary",
- light: boolean,
- size: "large" | "medium" | "small",
- theme: ButtonThemeContract,
- themeName: string,
-) => {
- const color: string =
- buttonColor === "destructive"
- ? theme.color.bg.critical.default
- : theme.color.bg.action.default;
+// // export for testing only
+// export const _generateStyles = (
+// buttonColor = "default",
+// kind: "primary" | "secondary" | "tertiary",
+// light: boolean,
+// size: "large" | "medium" | "small",
+// theme: ButtonThemeContract,
+// themeName: string,
+// ) => {
+// const color: string =
+// buttonColor === "destructive"
+// ? theme.color.bg.critical.default
+// : theme.color.bg.action.default;
- const buttonType = `${color}-${kind}-${light}-${size}-${themeName}`;
+// const buttonType = `${color}-${kind}-${light}-${size}-${themeName}`;
- if (styles[buttonType]) {
- return styles[buttonType];
- }
+// if (styles[buttonType]) {
+// return styles[buttonType];
+// }
- const fadedColor =
- buttonColor === "destructive"
- ? theme.color.bg.critical.inverse
- : theme.color.bg.action.inverse;
- const activeColor =
- buttonColor === "destructive"
- ? theme.color.bg.critical.active
- : theme.color.bg.action.active;
- const padding =
- size === "large" ? theme.padding.xLarge : theme.padding.large;
+// const fadedColor =
+// buttonColor === "destructive"
+// ? theme.color.bg.critical.inverse
+// : theme.color.bg.action.inverse;
+// const activeColor =
+// buttonColor === "destructive"
+// ? theme.color.bg.critical.active
+// : theme.color.bg.action.active;
+// const padding =
+// size === "large" ? theme.padding.xLarge : theme.padding.large;
- let newStyles: Record = {};
- if (kind === "primary") {
- const boxShadowInnerColor: string = light
- ? theme.color.bg.primary.inverse
- : theme.color.bg.primary.default;
+// let newStyles: Record = {};
+// if (kind === "primary") {
+// const boxShadowInnerColor: string = light
+// ? theme.color.bg.primary.inverse
+// : theme.color.bg.primary.default;
- newStyles = {
- default: {
- background: light ? theme.color.bg.primary.default : color,
- color: light ? color : theme.color.text.inverse,
- paddingLeft: padding,
- paddingRight: padding,
- },
- focus: {
- // This assumes a background of white for the regular button and
- // a background of darkBlue for the light version. The inner
- // box shadow/ring is also small enough for a slight variation
- // in the background color not to matter too much.
- boxShadow: `0 0 0 1px ${boxShadowInnerColor}, 0 0 0 3px ${
- light ? theme.color.bg.primary.default : color
- }`,
- },
- active: {
- boxShadow: `0 0 0 1px ${boxShadowInnerColor}, 0 0 0 3px ${
- light ? fadedColor : activeColor
- }`,
- background: light ? fadedColor : activeColor,
- color: light ? activeColor : fadedColor,
- },
- disabled: {
- background: light
- ? fadedColor
- : theme.color.bg.primary.disabled,
- color: light ? color : theme.color.text.primary.disabled,
- cursor: "default",
- ":focus": {
- boxShadow: `0 0 0 1px ${
- light
- ? theme.color.bg.primary.disabled
- : theme.color.bg.primary.default
- }, 0 0 0 3px ${
- light ? fadedColor : theme.color.bg.primary.disabled
- }`,
- },
- },
- };
- } else if (kind === "secondary") {
- const secondaryBorderColor =
- buttonColor === "destructive"
- ? theme.color.border.secondary.critical
- : theme.color.border.secondary.action;
- const secondaryActiveColor =
- buttonColor === "destructive"
- ? theme.color.bg.secondary.active.critical
- : theme.color.bg.secondary.active.action;
+// newStyles = {
+// default: {
+// background: light ? theme.color.bg.primary.default : color,
+// color: light ? color : theme.color.text.inverse,
+// paddingLeft: padding,
+// paddingRight: padding,
+// },
+// focus: {
+// // This assumes a background of white for the regular button and
+// // a background of darkBlue for the light version. The inner
+// // box shadow/ring is also small enough for a slight variation
+// // in the background color not to matter too much.
+// boxShadow: `0 0 0 1px ${boxShadowInnerColor}, 0 0 0 3px ${
+// light ? theme.color.bg.primary.default : color
+// }`,
+// },
+// active: {
+// boxShadow: `0 0 0 1px ${boxShadowInnerColor}, 0 0 0 3px ${
+// light ? fadedColor : activeColor
+// }`,
+// background: light ? fadedColor : activeColor,
+// color: light ? activeColor : fadedColor,
+// },
+// disabled: {
+// background: light
+// ? fadedColor
+// : theme.color.bg.primary.disabled,
+// color: light ? color : theme.color.text.primary.disabled,
+// cursor: "default",
+// ":focus": {
+// boxShadow: `0 0 0 1px ${
+// light
+// ? theme.color.bg.primary.disabled
+// : theme.color.bg.primary.default
+// }, 0 0 0 3px ${
+// light ? fadedColor : theme.color.bg.primary.disabled
+// }`,
+// },
+// },
+// };
+// } else if (kind === "secondary") {
+// const secondaryBorderColor =
+// buttonColor === "destructive"
+// ? theme.color.border.secondary.critical
+// : theme.color.border.secondary.action;
+// const secondaryActiveColor =
+// buttonColor === "destructive"
+// ? theme.color.bg.secondary.active.critical
+// : theme.color.bg.secondary.active.action;
- newStyles = {
- default: {
- background: light
- ? theme.color.bg.secondary.inverse
- : theme.color.bg.secondary.default,
- color: light ? theme.color.text.inverse : color,
- borderColor: light
- ? theme.color.border.secondary.inverse
- : secondaryBorderColor,
- borderStyle: "solid",
- borderWidth: theme.border.width.secondary,
- paddingLeft: padding,
- paddingRight: padding,
- },
- focus: {
- background: light
- ? theme.color.bg.secondary.inverse
- : theme.color.bg.secondary.focus,
- borderColor: "transparent",
- outlineColor: light
- ? theme.color.border.primary.inverse
- : color,
- outlineStyle: "solid",
- outlineWidth: theme.border.width.focused,
- },
+// newStyles = {
+// default: {
+// background: light
+// ? theme.color.bg.secondary.inverse
+// : theme.color.bg.secondary.default,
+// color: light ? theme.color.text.inverse : color,
+// borderColor: light
+// ? theme.color.border.secondary.inverse
+// : secondaryBorderColor,
+// borderStyle: "solid",
+// borderWidth: theme.border.width.secondary,
+// paddingLeft: padding,
+// paddingRight: padding,
+// },
+// focus: {
+// background: light
+// ? theme.color.bg.secondary.inverse
+// : theme.color.bg.secondary.focus,
+// borderColor: "transparent",
+// outlineColor: light
+// ? theme.color.border.primary.inverse
+// : color,
+// outlineStyle: "solid",
+// outlineWidth: theme.border.width.focused,
+// },
- active: {
- background: light ? activeColor : secondaryActiveColor,
- color: light ? fadedColor : activeColor,
- borderColor: "transparent",
- outlineColor: light ? fadedColor : activeColor,
- outlineStyle: "solid",
- outlineWidth: theme.border.width.focused,
- },
- disabled: {
- color: light
- ? theme.color.text.secondary.inverse
- : theme.color.text.disabled,
- outlineColor: light ? fadedColor : theme.color.border.disabled,
- cursor: "default",
- ":focus": {
- outlineColor: light
- ? theme.color.border.secondary.inverse
- : theme.color.border.disabled,
- outlineWidth: theme.border.width.disabled,
- },
- },
- };
- } else if (kind === "tertiary") {
- newStyles = {
- default: {
- background: "none",
- color: light ? theme.color.text.inverse : color,
- paddingLeft: 0,
- paddingRight: 0,
- },
- hover: {
- ":after": {
- content: "''",
- position: "absolute",
- height: theme.size.height.tertiaryHover,
- width: "100%",
- right: 0,
- bottom: 0,
- background: light ? theme.color.bg.tertiary.hover : color,
- borderRadius: theme.border.radius.tertiary,
- },
- },
- focus: {
- outlineStyle: "solid",
- outlineColor: light
- ? theme.color.border.tertiary.inverse
- : color,
- outlineWidth: theme.border.width.focused,
- borderRadius: theme.border.radius.default,
- },
- active: {
- color: light ? fadedColor : activeColor,
- ":after": {
- height: 1,
- background: light ? fadedColor : activeColor,
- },
- },
- disabled: {
- color: light ? fadedColor : theme.color.text.disabled,
- cursor: "default",
- },
- disabledFocus: {
- outlineColor: light
- ? theme.color.border.tertiary.inverse
- : theme.color.border.disabled,
- },
- };
- } else {
- throw new Error("Button kind not recognized");
- }
+// active: {
+// background: light ? activeColor : secondaryActiveColor,
+// color: light ? fadedColor : activeColor,
+// borderColor: "transparent",
+// outlineColor: light ? fadedColor : activeColor,
+// outlineStyle: "solid",
+// outlineWidth: theme.border.width.focused,
+// },
+// disabled: {
+// color: light
+// ? theme.color.text.secondary.inverse
+// : theme.color.text.disabled,
+// outlineColor: light ? fadedColor : theme.color.border.disabled,
+// cursor: "default",
+// ":focus": {
+// outlineColor: light
+// ? theme.color.border.secondary.inverse
+// : theme.color.border.disabled,
+// outlineWidth: theme.border.width.disabled,
+// },
+// },
+// };
+// } else if (kind === "tertiary") {
+// newStyles = {
+// default: {
+// background: "none",
+// color: light ? theme.color.text.inverse : color,
+// paddingLeft: 0,
+// paddingRight: 0,
+// },
+// hover: {
+// ":after": {
+// content: "''",
+// position: "absolute",
+// height: theme.size.height.tertiaryHover,
+// width: "100%",
+// right: 0,
+// bottom: 0,
+// background: light ? theme.color.bg.tertiary.hover : color,
+// borderRadius: theme.border.radius.tertiary,
+// },
+// },
+// focus: {
+// outlineStyle: "solid",
+// outlineColor: light
+// ? theme.color.border.tertiary.inverse
+// : color,
+// outlineWidth: theme.border.width.focused,
+// borderRadius: theme.border.radius.default,
+// },
+// active: {
+// color: light ? fadedColor : activeColor,
+// ":after": {
+// height: 1,
+// background: light ? fadedColor : activeColor,
+// },
+// },
+// disabled: {
+// color: light ? fadedColor : theme.color.text.disabled,
+// cursor: "default",
+// },
+// disabledFocus: {
+// outlineColor: light
+// ? theme.color.border.tertiary.inverse
+// : theme.color.border.disabled,
+// },
+// };
+// } else {
+// throw new Error("Button kind not recognized");
+// }
- styles[buttonType] = StyleSheet.create(newStyles);
- return styles[buttonType];
-};
+// styles[buttonType] = StyleSheet.create(newStyles);
+// return styles[buttonType];
+// };
diff --git a/packages/wonder-blocks-button/src/components/button.module.css b/packages/wonder-blocks-button/src/components/button.module.css
new file mode 100644
index 0000000000..849bf43b59
--- /dev/null
+++ b/packages/wonder-blocks-button/src/components/button.module.css
@@ -0,0 +1,535 @@
+.shared {
+ position: relative;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ padding-top: 0;
+ padding-bottom: 0;
+ padding-left: var(--button-padding-large);
+ padding-right: var(--button-padding-large);
+ border: none;
+ cursor: pointer;
+ outline: none;
+ text-decoration: none;
+ box-sizing: border-box;
+ /* This removes the 300ms click delay on mobile browsers by indicating that */
+ /* double-tap to zoom shouldn't be used on this element. */
+ touch-action: manipulation;
+ user-select: none;
+
+ &:focus {
+ /* // Mobile: Removes a blue highlight style shown when the user clicks a button */
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ }
+}
+
+.disabled {
+ cursor: auto;
+}
+
+/**
+ * sizes
+ */
+.medium {
+ border-radius: var(--button-border-radius-default);
+ height: var(--button-size-height-medium);
+}
+
+.small {
+ border-radius: var(--button-border-radius-small);
+ height: var(--button-size-height-small);
+}
+
+.large {
+ border-radius: var(--button-border-radius-large);
+ height: var(--button-size-height-large);
+}
+
+.text {
+ align-items: center;
+ font-weight: var(--button-font-weight-default);
+ white-space: nowrap;
+ overflow: hidden;
+ /* allows the button text to truncate */
+ display: inline-block;
+ /* fix Safari bug where the browser was eating mouse events */
+ pointer-events: none;
+}
+
+.largeText {
+ font-size: var(--button-font-size-large);
+ line-height: var(--button-line-height-large);
+}
+
+.textWithFocus {
+ /* allows the tertiary button border to use the label width */
+ position: relative;
+}
+
+.hiddenText {
+ visibility: hidden;
+}
+
+.spinner {
+ position: absolute;
+}
+
+.startIcon {
+ margin-right: var(--button-padding-small);
+ margin-left: var(--button-icon-offset);
+}
+
+.tertiaryStartIcon {
+ /* // Undo the negative padding from startIcon since tertiary
+ // buttons don't have extra padding. */
+ margin-left: 0;
+}
+
+.endIcon {
+ margin-left: var(--button-padding-small);
+}
+
+.iconWrapper {
+ border-radius: var(--button-border-radius-icon);
+ padding: var(--button-padding-small);
+ /* // View has a default minWidth of 0; which causes the label text
+ // to encroach on the icon when it needs to truncate. We can fix
+ // this by setting the minWidth to auto. */
+ min-width: auto;
+}
+
+.iconWrapperSecondaryHovered {
+ background-color: var(--button-color-bg-icon-secondaryHover);
+ color: var(--button-color-text-icon-secondaryHover);
+}
+
+.endIconWrapper {
+ margin-left: var(--button-padding-small);
+ margin-right: var(--button-margin-icon-offset);
+}
+
+.endIconWrapperTertiary {
+ margin-right: 0;
+}
+
+/**
+ * ------------------------------------------------------------
+ * _generateStyles
+ * ------------------------------------------------------------
+ */
+.default {
+ /* const color... */
+ --button-internal-color-default: var(--button-color-bg-action-default);
+ --button-internal-color-destructive: var(--button-color-bg-critical-default);
+ /* const fadedColor */
+ --button-internal-faded-color-default: var(--button-color-bg-action-inverse);
+ --button-internal-faded-color-destructive: var(--button-color-critical-inverse);
+ /* const activeColor */
+ --button-internal-active-color-default: var(--button-color-bg-action-active);
+ --button-internal-active-color-destructive: var(--button-color-critical-active);
+ /* const padding */
+ --button-internal-size-padding-default: var(--button-padding-large);
+ --button-internal-size-padding-large: var(--button-padding-xLarge);
+}
+
+/**
+ * ------------------------------------------------------------
+ * kind=primary
+ */
+.primary {
+ /* const boxShadowInnerColor */
+ --button-internal-box-shadow-inner-color: var(--button-color-bg-primary-default);
+ --button-internal-box-shadow-inner-color-inverse: var(--button-color-bg-primary-inverse);
+}
+
+/* kind=primary, color=default, light=false, state=resting */
+.primaryDefault {
+ background: var(--button-internal-color-default);
+ color: var(--button-color-text-inverse);
+ padding-left: var(--button-internal-size-padding-default);
+ padding-right: var(--button-internal-size-padding-default);
+}
+
+.primarySizeLarge {
+ padding-left: var(--button-internal-size-padding-large);
+ padding-right: var(--button-internal-size-padding-large);
+}
+
+/* kind=primary, color=default, light=true, state=resting */
+.primaryDefaultLight {
+ background: var(--button-color-bg-primary-default);
+ color: var(--button-internal-color-default);
+}
+
+/* kind=primary, color=destructive, state=resting, light=false */
+.primaryDestructive {
+ background: var(--button-internal-color-destructive);
+}
+
+/* kind=primary, color=destructive, state=resting, light=true */
+.primaryDestructiveLight {
+ /* duplicate */
+ background: var(--button-color-bg-primary-default);
+ color: var(--button-internal-color-destructive);
+}
+
+/* kind=primary, color=default, light=false, state=focus */
+.primaryDefaultFocus {
+ box-shadow: 0 0 0 1px var(--button-internal-box-shadow-inner-color), 0 0 0 3px var(--button-internal-color-default);
+}
+
+/* kind=primary, color=default, light=true, state=focus */
+.primaryDefaultLightFocus {
+ box-shadow: 0 0 0 1px var(--button-internal-box-shadow-inner-color-inverse), 0 0 0 3px var(--button-internal-color-default);
+}
+
+/* kind=primary, color=destructive, light=false, state=focus */
+.primaryDestructiveFocus {
+ box-shadow: 0 0 0 1px var(--button-internal-box-shadow-inner-color), 0 0 0 3px var(--button-internal-color-destructive);
+
+}
+
+/* kind=primary, color=destructive, light=true, state=focus */
+.primaryDestructiveLightFocus {
+ /* NOTE(juan): same as default (aka primary-focus-light)... MIGHT DELETE */
+ box-shadow: 0 0 0 1px var(--button-internal-box-shadow-inner-color-inverse), 0 0 0 3px var(--button-internal-color-destructive);
+}
+
+/* kind=primary, color=default, light=false, state=active */
+.primaryDefaultActive {
+ box-shadow: 0 0 0 1px var(--button-internal-box-shadow-inner-color), 0 0 0 3px var(--button-internal-active-color-default);
+ background: var(--button-internal-active-color-default);
+ color: var(--button-internal-faded-color-default);
+}
+
+/* kind=primary, color=default, light=true, state=active */
+.primaryDefaultLightActive {
+ box-shadow: 0 0 0 1px var(--button-internal-box-shadow-inner-color-inverse), 0 0 0 3px var(--button-internal-faded-color-default);
+ background: var(--button-color-bg-action-inverse);
+ color: var(--button-color-bg-action-active);
+}
+
+/* kind=primary, color=destructive, light=false, state=active */
+.primaryDestructiveActive {
+ box-shadow: 0 0 0 1px var(--button-internal-box-shadow-inner-color), 0 0 0 3px var(--button-internal-active-color-destructive);
+ background: var(--button-internal-active-color-destructive);
+ color: var(--button-internal-faded-color-destructive);
+}
+
+/* kind=primary, color=destructive, light=true, state=active */
+.primaryDestructiveLightActive {
+ box-shadow: 0 0 0 1px var(--button-internal-box-shadow-inner-color-inverse), 0 0 0 3px var(--button-internal-faded-color-destructive);
+ background: var(--button-color-bg-critical-inverse);
+ color: var(--button-color-bg-critical-active);
+}
+
+/* kind=primary, color=default, light=false, state=disabled */
+.primaryDefaultDisabled {
+ background: var(--button-color-bg-primary-disabled);
+ color: var(--button-color-text-primary-disabled);
+ cursor: default;
+}
+
+.primaryDefaultDisabled:focus {
+ box-shadow: 0 0 0 1px var(--button-color-bg-primary-default), 0 0 0 3px var(--button-color-bg-primary-disabled);
+}
+
+/* kind=primary, color=default, light=true, state=disabled */
+.primaryDefaultLightDisabled {
+ background: var(--button-color-bg-action-inverse);
+ color: var(--button-internal-color-default);
+}
+
+.primaryDefaultLightDisabled:focus {
+ box-shadow: 0 0 0 1px var(--button-color-bg-primary-disabled), 0 0 0 3px var(--button-internal-faded-color-default);
+}
+
+/* kind=primary, color=destructive, light=false, state=disabled */
+.primaryDestructiveDisabled {
+ /* NOTE(juan): same as default color... MIGHT DELETE */
+ background: var(--button-color-bg-primary-disabled);
+ color: var(--button-internal-color-destructive);
+ cursor: default;
+}
+
+.primaryDestructiveDisabled:focus {
+ /* NOTE(juan): same as default color... MIGHT DELETE */
+ box-shadow: 0 0 0 1px var(--button-color-bg-primary-default), 0 0 0 3px var(--button-color-bg-primary-disabled);
+}
+
+/* kind=primary, color=destructive, light=true, state=disabled */
+.primaryDestructiveLightDisabled {
+ background: var(--button-internal-faded-color-destructive);
+ color: var(--button-internal-color-destructive);
+}
+
+.primaryDestructiveLightDisabled:focus {
+ box-shadow: 0 0 0 1px var(--button-color-bg-primary-disabled), 0 0 0 3px var(--button-internal-faded-color-destructive);
+}
+
+/**
+ * ------------------------------------------------------------
+ * kind=secondary
+ */
+
+/* secondary:resting */
+.secondary {
+ /* custom css variables */
+ --secondary-border-color-default: var(--button-color-border-secondary-action);
+ --secondary-border-color-destructive: var(--button-color-border-secondary-critical);
+ --secondary-active-color-default: var(--button-color-bg-secondary-active-action);
+ --secondary-active-color-destructive: var(--button-color-bg-secondary-active-critical);
+
+ /* base styles */
+ background: var(--button-color-bg-secondary-default);
+ border-style: solid;
+ border-width: var(--button-border-width-secondary);
+ padding-left: var(--button-internal-size-padding-default);
+ padding-right: var(--button-internal-size-padding-default);
+}
+
+/* kind=secondary, color=default, light=false, state=resting */
+.secondaryDefault {
+ color: var(--button-internal-color-default);
+ border-color: var(--secondary-border-color-default);
+}
+
+/* kind=secondary, color=destructive, light=false, state=resting */
+.secondaryDestructive {
+ color: var(--button-internal-color-destructive);
+ border-color: var(--secondary-border-color-destructive);
+}
+
+/* kind=secondary, color=default&destructive, light=true, state=resting */
+.secondaryLight {
+ background: var(--button-color-bg-secondary-inverse);
+ color: var(--button-color-text-inverse);
+ border-color: var(--button-color-border-secondary-inverse);
+}
+
+/* secondary:focus */
+.secondaryFocus {
+ background: var(--button-color-bg-secondary-focus);
+ border-color: transparent;
+ outline-style: solid;
+ outline-width: var(--button-border-width-focused);
+}
+
+/* kind=secondary, color=default, light=false, state=focus */
+.secondaryDefaultFocus {
+ outline-color: var(--button-internal-color-default);
+}
+
+/* kind=secondary, color=destructive, light=false, state=focus */
+.secondaryDestructiveFocus {
+ outline-color: var(--button-internal-color-destructive);
+}
+
+/* kind=secondary, color=default&destructive, light=true, state=focus */
+.secondaryLightFocus {
+ background: var(--button-color-bg-secondary-inverse);
+ outline-color: var(--button-color-border-primary-inverse);
+}
+
+/* secondary:active */
+.secondaryActive {
+ border-color: transparent;
+ outline-style: solid;
+ outline-width: var(--button-border-width-focused);
+}
+
+/* kind=secondary, color=default, light=false, state=active */
+.secondaryDefaultActive {
+ background: var(--secondary-active-color-default);
+ color: var(--button-internal-active-color-default);
+ outline-color: var(--button-internal-active-color-default);
+}
+
+/* kind=secondary, color=destructive, light=false, state=active */
+.secondaryDestructiveActive {
+ background: var(--secondary-active-color-destructive);
+ color: var(--button-internal-active-color-destructive);
+ outline-color: var(--button-internal-active-color-destructive);
+}
+
+/* kind=secondary, color=default, light=true, state=active */
+.secondaryDefaultLightActive {
+ background: var(--button-internal-active-color-default);
+ color: var(--button-internal-faded-color-default);
+ outline-color: var(--button-internal-faded-color-default);
+}
+
+/* kind=secondary, color=destructive, light=true, state=active */
+.secondaryDestructiveLightActive {
+ background: var(--button-internal-active-color-destructive);
+ color: var(--button-internal-faded-color-destructive);
+ outline-color: var(--button-internal-faded-color-destructive);
+}
+
+/* secondary:disabled */
+.secondaryDisabled {
+ color: var(--button-color-text-disabled);
+ outline-color: var(--button-color-border-disabled);
+ cursor: default;
+}
+
+.secondaryDisabled:focus {
+ outline-color: var(--button-color-border-disabled);
+ outline-width: var(--button-border-width-disabled);
+}
+
+/* kind=secondary, color=default, light=true, state=disabled */
+.secondaryDefaultLightDisabled {
+ color: var(--button-color-text-secondary-inverse);
+ outline-color: var(--button-internal-faded-color-default);
+}
+
+.secondaryDestructiveLightDisabled {
+ color: var(--button-color-text-secondary-inverse);
+ outline-color: var(--button-internal-faded-color-destructive);
+}
+
+.secondaryLightDisabled:focus {
+ outline-color: var(--button-color-border-secondary-inverse);
+}
+
+/**
+ * ------------------------------------------------------------
+ * kind=tertiary
+ */
+
+/* tertiary:resting */
+.tertiary {
+ background: none;
+ padding-left: 0;
+ padding-right: 0;
+}
+
+/* kind=tertiary, color=default, light=false, state=resting */
+.tertiaryDefault {
+ color: var(--button-internal-color-default);
+}
+
+/* kind=tertiary, color=destructive, light=false, state=resting */
+.tertiaryDestructive {
+ color: var(--button-internal-color-destructive);
+}
+
+/* kind=tertiary, color=default&destructive, light=true, state=resting */
+.tertiaryLight {
+ color: var(--button-color-text-inverse);
+}
+
+/* tertiary:hover */
+.tertiaryHover:after {
+ content: '';
+ position: absolute;
+ height: var(--button-size-height-tertiaryHover);
+ width: 100%;
+ right: 0;
+ bottom: 0;
+ border-radius: var(--button-border-radius-tertiary);
+}
+
+/* kind=tertiary, color=default, light=false, state=hover */
+.tertiaryDefaultHover:after {
+ background: var(--button-internal-color-default);
+}
+
+/* kind=tertiary, color=destructive, light=false, state=hover */
+.tertiaryDestructiveHover:after {
+ background: var(--button-internal-color-destructive);
+}
+
+/* kind=tertiary, color=default&destructive, light=true, state=hover */
+.tertiaryLightHover:after {
+ background: var(--button-color-bg-tertiary-hover);
+}
+
+/* tertiary:focus */
+.tertiaryFocus {
+ outline-style: solid;
+ outline-width: var(--button-border-width-focused);
+ border-radius: var(--button-border-radius-default);
+}
+
+/* kind=tertiary, color=default, light=false, state=focus */
+.tertiaryDefaultFocus {
+ outline-color: var(--button-internal-color-default);
+}
+
+/* kind=tertiary, color=destructive, light=false, state=focus */
+.tertiaryDestructiveFocus {
+ outline-color: var(--button-internal-color-destructive);
+}
+
+/* kind=tertiary, color=default&destructive, light=true, state=focus */
+.tertiaryLightFocus {
+ outline-color: var(--button-color-border-tertiary-inverse);
+}
+
+/* tertiary:active */
+.tertiaryActive:after {
+ height: 1;
+}
+
+/* kind=tertiary, color=default, light=false, state=active */
+.tertiaryDefaultActive {
+ color: var(--button-internal-active-color-default);
+}
+
+.tertiaryDefaultActive:after {
+ background: var(--button-internal-active-color-default);
+}
+
+/* kind=tertiary, color=destructive, light=false, state=active */
+.tertiaryDestructiveActive {
+ color: var(--button-internal-active-color-destructive);
+}
+
+.tertiaryDestructiveActive:after {
+ background: var(--button-internal-active-color-destructive);
+}
+
+/* kind=tertiary, color=default, light=true, state=active */
+.tertiaryDefaultLightActive {
+ color: var(--button-internal-faded-color-default);
+}
+
+.tertiaryDefaultLightActive:after {
+ background: var(--button-internal-faded-color-default);
+}
+
+/* kind=tertiary, color=destructive, light=true, state=active */
+.tertiaryDestructiveLightActive {
+ color: var(--button-internal-faded-color-destructive);
+}
+
+.tertiaryDestructiveLightActive:after {
+ background: var(--button-internal-faded-color-destructive);
+}
+
+/* tertiary:disabled */
+.tertiaryDisabled {
+ color: var(--button-color-text-disabled);
+ cursor: default;
+}
+
+/* kind=tertiary, color=default, light=true, state=disabled */
+.tertiaryDefaultLightDisabled {
+ color: var(--button-internal-faded-color-default);
+}
+
+/* kind=tertiary, color=destructive, light=true, state=disabled */
+.tertiaryDestructiveLightDisabled {
+ color: var(--button-internal-faded-color-destructive);
+}
+
+/* tertiary:disabled+focus */
+.tertiaryDisabledFocus {
+ outline-color: var(--button-color-border-disabled);
+}
+
+.tertiaryLightDisabledFocus {
+ outline-color: var(--button-color-border-tertiary-inverse);
+}
\ No newline at end of file
diff --git a/packages/wonder-blocks-button/src/themes/default.module.css b/packages/wonder-blocks-button/src/themes/default.module.css
new file mode 100644
index 0000000000..faa0eeaeed
--- /dev/null
+++ b/packages/wonder-blocks-button/src/themes/default.module.css
@@ -0,0 +1,105 @@
+.theme {
+ /**
+ * Background
+ */
+ /* color="default" */
+ --button-color-bg-action-default: var(--wb-color-blue);
+ --button-color-bg-action-active: var(--wb-color-activeBlue);
+ --button-color-bg-action-inverse: var(--wb-color-fadedBlue);
+ /* color="destructive */
+ --button-color-bg-critical-default: var(--wb-color-red);
+ --button-color-bg-critical-active: var(--wb-color-activeRed);
+ --button-color-bg-critical-inverse: var(--wb-color-fadedRed);
+
+ /* kind="primary" */
+ --button-color-bg-primary-default: var(--wb-color-white);
+ --button-color-bg-primary-disabled: var(--wb-color-offBlack32);
+ --button-color-bg-primary-inverse: var(--wb-color-darkBlue);
+
+ /* kind="secondary" */
+ --button-color-bg-secondary-default: none;
+ --button-color-bg-secondary-inverse: none;
+ --button-color-bg-secondary-focus: var(--wb-color-white);
+ --button-color-bg-secondary-active-action: var(--wb-color-fadedBlue);
+ --button-color-bg-secondary-active-critical: var(--wb-color-fadedRed);
+
+ /* kind="tertiary" */
+ --button-color-bg-tertiary-hover: var(--wb-color-white);
+
+ /* icons */
+ --button-color-bg-icon-secondaryHover: transparent;
+
+ /**
+ * Text color
+ */
+ /* color="default" */
+ --button-color-text-disabled: var(--wb-color-offBlack32);
+ --button-color-text-inverse: var(--wb-color-white);
+
+ /* kind */
+ --button-color-text-primary-disabled: var(--wb-color-white64);
+ --button-color-text-secondary-inverse: var(--wb-color-white50);
+
+ /* icons */
+ --button-color-text-icon-secondaryHover: var(--wb-color-blue);
+
+ /**
+ * Border color
+ */
+ /* color="default" */
+ --button-color-border-disabled: var(--wb-color-offBlack32);
+
+ /* kind */
+ --button-color-border-primary-inverse: var(--wb-color-white);
+ --button-color-border-secondary-action: var(--wb-color-offBlack50);
+ --button-color-border-secondary-critical: var(--wb-color-offBlack50);
+ --button-color-border-secondary-inverse: var(--wb-color-white50);
+ --button-color-border-tertiary-inverse: var(--wb-color-white50);
+
+ /**
+ * Border
+ */
+ /* width */
+ --button-border-width-secondary: var(--wb-border-width-hairline);
+ --button-border-width-focused: var(--wb-border-width-thin);
+ --button-border-width-disabled: var(--wb-border-width-thin);
+
+ /* radius */
+ --button-border-radius-default: var(--wb-border-radius-medium_4);
+ --button-border-radius-tertiary: var(--wb-border-radius-xSmall_2);
+ --button-border-radius-small: var(--wb-border-radius-medium_4);
+ --button-border-radius-large: var(--wb-border-radius-large_6);
+ --button-border-radius-icon: var(--wb-border-radius-full);
+
+ /**
+ * Size
+ */
+ --button-size-height-tertiaryHover: var(--wb-spacing-xxxxSmall_2);
+ --button-size-height-small: var(--wb-spacing-xLarge_32);
+ /* NOTE: These height tokens are specific to this component. */
+ --button-size-height-medium: 40px;
+ --button-size-height-large: 56px;
+
+ /**
+ * Margin
+ */
+ --button-margin-icon-offset: calc(var(--wb-spacing-xxxxSmall_2)*-1);
+
+ /**
+ * Padding
+ */
+ --button-padding-xsmall: var(--wb-spacing-xxxxSmall_2);
+ --button-padding-small: var(--wb-spacing-xxSmall6);
+ --button-padding-medium: var(--wb-spacing-small_12);
+ --button-padding-large: var(--wb-spacing-medium_16);
+ --button-padding-xlarge: var(--wb-spacing-xLarge_32);
+
+ /**
+ * Font
+ */
+
+ /* NOTE: This token is specific to this button size. */
+ --button-font-size-large: 18;
+ --button-font-lineHeight-large: var(--wb-lineHeight-medium);
+ --button-font-weight-default: var(--wb-font-weight-bold);
+}
\ No newline at end of file
diff --git a/packages/wonder-blocks-button/src/themes/khanmigo.module.css b/packages/wonder-blocks-button/src/themes/khanmigo.module.css
new file mode 100644
index 0000000000..159057bfc9
--- /dev/null
+++ b/packages/wonder-blocks-button/src/themes/khanmigo.module.css
@@ -0,0 +1,49 @@
+.theme {
+ /**
+ * Background
+ */
+ /* kind="secondary" */
+ --button-color-bg-secondary-default: var(--wb-color-offWhite);
+ --button-color-bg-secondary-active-action: var(--wb-color-fadedBlue8);
+ --button-color-bg-secondary-active-critical: var(--wb-color-fadedRed8);
+ --button-color-bg-secondary-focus: var(--wb-color-offWhite);
+
+ /* kind="tertiary" */
+ --button-color-bg-tertiary-hover: var(--wb-color-white);
+
+ /* icons */
+ --button-color-bg-icon-secondaryHover: var(--wb-color-fadedBlue16);
+
+ /**
+ * Text color
+ */
+ --button-color-text-icon-secondaryHover: var(--wb-color-blue);
+
+ /**
+ * Border color
+ */
+ /* kind */
+ --button-color-border-secondary-action: var(--wb-color-fadedBlue);
+ --button-color-border-secondary-critical: var(--wb-color-fadedRed);
+
+ /**
+ * Border
+ */
+ /* width */
+ --button-border-width-focused: var(--wb-border-width-hairline);
+
+ /* radius */
+ --button-border-radius-default: var(--wb-border-radius-xLarge_12);
+ --button-border-radius-small: var(--wb-border-radius-large_6);
+ --button-border-radius-large: var(--wb-border-radius-xLarge_12);
+
+ /**
+ * Margin
+ */
+ --button-margin-icon-offset: calc(var(--wb-spacing-xSmall_8) * -1);
+
+ /**
+ * Font
+ */
+ --button-font-weight-default: var(--wb-font-weight-regular);
+}
\ No newline at end of file
diff --git a/packages/wonder-blocks-button/src/themes/themed-button.tsx b/packages/wonder-blocks-button/src/themes/themed-button.tsx
index 0402516b8d..b016a8a30d 100644
--- a/packages/wonder-blocks-button/src/themes/themed-button.tsx
+++ b/packages/wonder-blocks-button/src/themes/themed-button.tsx
@@ -5,21 +5,21 @@ import {
ThemeSwitcherContext,
} from "@khanacademy/wonder-blocks-theming";
-import defaultTheme from "./default";
-import khanmigoTheme from "./khanmigo";
+import defaultTheme from "./default.module.css";
+import khanmigoTheme from "./khanmigo.module.css";
type Props = {
children: React.ReactNode;
};
-export type ButtonThemeContract = typeof defaultTheme;
+// export type ButtonThemeContract = typeof defaultTheme;
/**
* The themes available to the Button component.
*/
-const themes: Themes = {
- default: defaultTheme,
- khanmigo: khanmigoTheme,
+const themes: Themes = {
+ default: defaultTheme.theme,
+ khanmigo: khanmigoTheme.theme,
};
/**
@@ -34,7 +34,15 @@ export const ButtonThemeContext = createThemeContext(defaultTheme);
export default function ThemedButton(props: Props) {
const currentTheme = React.useContext(ThemeSwitcherContext);
- const theme = themes[currentTheme] || defaultTheme;
+ const theme =
+ currentTheme !== "default"
+ ? // HACK(juan): There's no way to merge themes, so we're just
+ // concatenating the class names. This case is for when the button
+ // is using a different theme than the default one (like
+ // `khanmigo`).
+ themes.default + " " + themes[currentTheme]
+ : themes.default;
+
return (
{props.children}
diff --git a/yarn.lock b/yarn.lock
index 1a05e01b2f..1e7f21c7a8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6107,6 +6107,13 @@ cjs-module-lexer@^1.0.0:
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40"
integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==
+class-variance-authority@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.0.tgz#1c3134d634d80271b1837452b06d821915954522"
+ integrity sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==
+ dependencies:
+ clsx "2.0.0"
+
classnames@^2.2.6:
version "2.3.2"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924"
@@ -6183,6 +6190,11 @@ clone@^1.0.2:
version "1.0.4"
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
+clsx@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.0.0.tgz#12658f3fd98fafe62075595a5c30e43d18f3d00b"
+ integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==
+
co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"