From 9c9bdc816cd7caaef43fc05b32ecd89b18174226 Mon Sep 17 00:00:00 2001 From: Fernanda Castillo Date: Fri, 29 Nov 2024 11:24:14 +0000 Subject: [PATCH 1/2] ghost first try --- .../ghost-button/ghost-button.stories.tsx | 196 ++++++++++++++++++ packages/lab/src/ghost-button/GhostButton.css | 144 +++++++++++++ packages/lab/src/ghost-button/GhostButton.tsx | 83 ++++++++ packages/lab/src/ghost-button/index.ts | 1 + packages/lab/src/index.ts | 1 + 5 files changed, 425 insertions(+) create mode 100644 packages/core/stories/ghost-button/ghost-button.stories.tsx create mode 100644 packages/lab/src/ghost-button/GhostButton.css create mode 100644 packages/lab/src/ghost-button/GhostButton.tsx create mode 100644 packages/lab/src/ghost-button/index.ts diff --git a/packages/core/stories/ghost-button/ghost-button.stories.tsx b/packages/core/stories/ghost-button/ghost-button.stories.tsx new file mode 100644 index 0000000000..9bac1cc16f --- /dev/null +++ b/packages/core/stories/ghost-button/ghost-button.stories.tsx @@ -0,0 +1,196 @@ +import { Banner, FlowLayout, StackLayout } from "@salt-ds/core"; +import { CloseIcon, SearchIcon } from "@salt-ds/icons"; +import { GhostButton, SystemStatus } from "@salt-ds/lab"; +import type { Meta, StoryFn } from "@storybook/react"; + +export default { + title: "Lab/GhostButton", + component: GhostButton, + argTypes: { onClick: { action: "clicked" } }, +} as Meta; + +export const TestingEverything: StoryFn = (props) => { + return ( + +
+ + Button + +
+ + + Button + + + + + Button + + + + + Button + + + + + Button + + + + + Button + + + + + Button + + + + + Button + + + + + Button + + +
+ + Button + +
+ + + Button + + + + + Button + + + + + Button + + + + + Button + + +
+ ); +}; + +const ButtonGridTemplate: StoryFn = (props) => { + return ( + + Submit + + + + + Search + + + Submit + + + + + Search + + + + Submit + + + + + Search + + + + ); +}; + +export const Disabled: StoryFn = (props) => { + return ( + + + Submit + + + + + Search + + + Submit + + + + + Search + + + + Submit + + + + + Search + + + + + + Submit + + + + + + Search + + + + Submit + + + + + + Search + + + + + Submit + + + + + + Search + + + + + ); +}; + +export const Solid = ButtonGridTemplate.bind({}); +Solid.args = { + adaptiveAppearance: "solid", +}; + +export const NonSolid = ButtonGridTemplate.bind({}); +NonSolid.args = { + adaptiveAppearance: "non-solid", +}; diff --git a/packages/lab/src/ghost-button/GhostButton.css b/packages/lab/src/ghost-button/GhostButton.css new file mode 100644 index 0000000000..c353b371eb --- /dev/null +++ b/packages/lab/src/ghost-button/GhostButton.css @@ -0,0 +1,144 @@ +/* TODO: sort and rename*/ + +.salt-theme.salt-theme-next[data-mode="light"] { + --salt-palette-alpha-weaky: var(--salt-color-black-20a); + + --salt-palette-alpha-hover-alt: var(--salt-color-black-10a); + --salt-palette-alpha-active-alt: var(--salt-color-black-20a); +} +.salt-theme.salt-theme-next[data-mode="dark"] { + --salt-palette-alpha-weaky: var(--salt-color-white-20a); + + --salt-palette-alpha-hover-alt: var(--salt-color-black-10a); + --salt-palette-alpha-active-alt: var(--salt-color-black-20a); +} + +.salt-theme[data-mode="light"] { + --salt-palette-alpha-weaky: var(--salt-color-black-fade-background-selection); + --salt-palette-alpha-weaker: var(--salt-color-black-fade-background-hover); + --salt-palette-alpha-hover-alt: var(--salt-color-black-fade-background-hover); + --salt-palette-alpha-active-alt: var(--salt-color-black-fade-background-selection); +} + +.salt-theme[data-mode="dark"] { + --salt-palette-alpha-weaky: var(--salt-color-white-fade-background-selection); + --salt-palette-alpha-weaker: var(--salt-color-white-fade-background-hover); + --salt-palette-alpha-hover-alt: var(--salt-color-black-fade-background-hover); + --salt-palette-alpha-active-alt: var(--salt-color-black-fade-background-selection); +} +.salt-theme { + --salt-color-white-fade-background-hover: rgba(255, 255, 255, var(--salt-opacity-8)); + --salt-color-white-fade-background-selection: rgba(255, 255, 255, var(--salt-opacity-15)); +} +.salt-theme, +.salt-theme-next { + --salt-actionable-alpha-background-hover-alt: var(--salt-palette-alpha-hover-alt); + --salt-actionable-alpha-background-active-alt: var(--salt-palette-alpha-active-alt); + --salt-actionable-alpha-background-hover: var(--salt-palette-alpha-weaker); + --salt-actionable-alpha-background-active: var(--salt-palette-alpha-weaky); +} + +/* ------------------------------- */ + +.saltGhostButton { + align-items: var(--saltGhostButton-alignItems, center); + appearance: none; + background: var(--saltGhostButton-background, var(--ghostButton-background)); + border-color: var(--saltGhostButton-borderColor, var(--ghostButton-borderColor, transparent)); + border-style: var(--saltGhostButton-borderStyle, solid); + border-width: var(--saltGhostButton-borderWidth, var(--salt-size-border, 0)); + border-radius: var(--saltGhostButton-borderRadius, var(--salt-palette-corner-weak, 0)); + color: var(--saltGhostButton-text-color, var(--ghostButton-text-color)); + cursor: var(--saltGhostButton-cursor, pointer); + display: inline-flex; + gap: var(--salt-spacing-50); + justify-content: var(--saltGhostButton-justifyContent, center); + font-size: var(--saltGhostButton-fontSize, var(--salt-text-fontSize)); + font-family: var(--saltGhostButton-fontFamily, var(--salt-text-action-fontFamily)); + line-height: var(--saltGhostButton-lineHeight, var(--salt-text-lineHeight)); + letter-spacing: var(--saltGhostButton-letterSpacing, var(--salt-text-action-letterSpacing)); + text-transform: var(--saltGhostButton-textTransform, var(--salt-text-action-textTransform)); + padding: 0 var(--saltGhostButton-padding, calc(var(--salt-spacing-100) - var(--saltGhostButton-borderWidth, var(--salt-size-border, 0)))); + margin: var(--saltGhostButton-margin, 0); + height: var(--saltGhostButton-height, var(--salt-size-base)); + min-width: var(--saltGhostButton-minWidth, unset); + position: relative; + text-align: var(--saltGhostButton-textAlign, var(--salt-text-action-textAlign)); + text-decoration: none; + transition: none; + width: var(--saltGhostButton-width, auto); + -webkit-appearance: none; + -webkit-tap-highlight-color: transparent; + font-weight: var(--saltGhostButton-fontWeight, var(--salt-text-action-fontWeight)); +} + +/* Pseudo-class applied to the root element on focus */ +.saltGhostButton:focus-visible { + outline-style: var(--salt-focused-outlineStyle); + outline-width: var(--salt-focused-outlineWidth); + outline-color: var(--salt-focused-outlineColor); + outline-offset: var(--salt-focused-outlineOffset); + background: var(--saltGhostButton-background-hover, var(--ghostButton-background-hover)); + color: var(--saltGhostButton-text-color-hover, var(--ghostButton-text-color-hover)); +} + +/* Pseudo-class applied to the root element on focus when Button is active */ +.saltGhostButton.saltGhostButton-active:focus-visible, +.saltGhostButton:focus-visible:active { + background: var(--saltGhostButton-background-active-hover, var(--ghostButton-background)); + color: var(--saltGhostButton-text-color-active-hover, var(--ghostButton-text-color)); +} + +/* Pseudo-class applied to the root element on hover when Button is not active or disabled */ +.saltGhostButton:hover { + background: var(--saltGhostButton-background-hover, var(--ghostButton-background-hover)); + color: var(--saltGhostButton-text-color-hover, var(--ghostButton-text-color-hover)); +} + +/* Pseudo-class applied to the root element when Button is active and not disabled */ +.saltGhostButton:active, +.saltGhostButton.saltGhostButton-active { + background: var(--saltGhostButton-background-active, var(--ghostButton-background-active)); + color: var(--saltGhostButton-text-color-active, var(--ghostButton-text-color-active)); +} + +/* Styles applied when the button triggers a dialog or menu */ +.saltGhostButton[aria-expanded="true"][aria-haspopup="menu"], +.saltGhostButton[aria-expanded="true"][aria-haspopup="dialog"] { + background: var(--saltGhostButton-background-active, var(--ghostButton-background-active)); + color: var(--saltGhostButton-text-color-active, var(--ghostButton-text-color-active)); + border-color: var(--saltGhostButton-borderColor-active, var(--ghostButton-borderColor-active)); +} + +/* Pseudo-class applied to the root element if disabled={true} */ +.saltGhostButton:disabled, +.saltGhostButton-disabled, +.saltGhostButton-disabled:active, +.saltGhostButton-disabled:focus-visible, +.saltGhostButton-disabled:focus-visible:active, +.saltGhostButton-disabled:hover { + background: var(--saltGhostButton-background-disabled, var(--ghostButton-background-disabled)); + color: var(--saltGhostButton-text-color-disabled, var(--ghostButton-text-color-disabled)); + cursor: var(--saltGhostButton-cursor-disabled, var(--salt-actionable-cursor-disabled)); +} + +/* TODO: update tokens*/ +.saltGhostButton { + --ghostButton-text-color: var(--salt-content-primary-foreground); + --ghostButton-text-color-hover: var(--salt-content-primary-foreground); + --ghostButton-text-color-active: var(--salt-content-primary-foreground); + --ghostButton-text-color-disabled: var(--salt-content-primary-foreground-disabled); + + --ghostButton-background-active: var(--salt-actionable-alpha-background-active); + --ghostButton-background-hover: var(--salt-actionable-alpha-background-hover); +} + +.saltGhostButton.saltGhostButton-solid { + --ghostButton-text-color: var(--salt-content-bold-foreground); + --ghostButton-text-color-hover: var(--salt-content-bold-foreground); + --ghostButton-text-color-active: var(--salt-content-bold-foreground); + --ghostButton-text-color-disabled: var(--salt-content-bold-foreground-disabled); + + --ghostButton-background-active: var(--salt-actionable-alpha-background-active-alt); + --ghostButton-background-hover: var(--salt-actionable-alpha-background-hover-alt); +} diff --git a/packages/lab/src/ghost-button/GhostButton.tsx b/packages/lab/src/ghost-button/GhostButton.tsx new file mode 100644 index 0000000000..1c6d971f2b --- /dev/null +++ b/packages/lab/src/ghost-button/GhostButton.tsx @@ -0,0 +1,83 @@ +import { useComponentCssInjection } from "@salt-ds/styles"; +import { useWindow } from "@salt-ds/window"; +import { clsx } from "clsx"; +import { + type ComponentPropsWithoutRef, + type ReactElement, + forwardRef, +} from "react"; + +import { makePrefixer, useButton } from "@salt-ds/core"; +import ghostButtonCss from "./GhostButton.css"; + +const withBaseName = makePrefixer("saltGhostButton"); + +export interface GhostButtonProps extends ComponentPropsWithoutRef<"button"> { + /** + * If `true`, the button will be disabled. + */ + disabled?: boolean; + /** + * If `true`, the button will be focusable when disabled. + */ + focusableWhenDisabled?: boolean; + /** + * The appearance of the button. + */ + adaptiveAppearance?: "solid" | "non-solid"; +} + +export const GhostButton = forwardRef( + function GhostButton( + { + children, + className, + disabled = false, + adaptiveAppearance = "non-solid", + focusableWhenDisabled, + onKeyUp, + onKeyDown, + onBlur, + onClick, + type = "button", + ...restProps + }, + ref, + ): ReactElement { + const { active, buttonProps } = useButton({ + disabled, + focusableWhenDisabled, + onKeyUp, + onKeyDown, + onBlur, + onClick, + }); + + const targetWindow = useWindow(); + useComponentCssInjection({ + testId: "salt-ghost-button", + css: ghostButtonCss, + window: targetWindow, + }); + + return ( + + ); + }, +); diff --git a/packages/lab/src/ghost-button/index.ts b/packages/lab/src/ghost-button/index.ts new file mode 100644 index 0000000000..c55973bee5 --- /dev/null +++ b/packages/lab/src/ghost-button/index.ts @@ -0,0 +1 @@ +export * from "./GhostButton"; diff --git a/packages/lab/src/index.ts b/packages/lab/src/index.ts index ec404c772e..1a23eb5452 100644 --- a/packages/lab/src/index.ts +++ b/packages/lab/src/index.ts @@ -40,6 +40,7 @@ export { export * from "./form-field-context-legacy"; export * from "./form-group"; export * from "./formatted-input"; +export * from "./ghost-button"; export { InputLegacy as Input, type InputLegacyProps as InputProps, From 1729fa4d0ccddb23a501059323a5fe17a469be7a Mon Sep 17 00:00:00 2001 From: Fernanda Castillo Date: Mon, 2 Dec 2024 15:55:58 +0000 Subject: [PATCH 2/2] ghost first try, comments --- .../ghost-button/ghost-button.stories.tsx | 28 ++++++++++++------- packages/lab/src/ghost-button/GhostButton.css | 7 +++++ packages/theme/css/foundations/opacity.css | 3 +- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/packages/core/stories/ghost-button/ghost-button.stories.tsx b/packages/core/stories/ghost-button/ghost-button.stories.tsx index 9bac1cc16f..0ca6754480 100644 --- a/packages/core/stories/ghost-button/ghost-button.stories.tsx +++ b/packages/core/stories/ghost-button/ghost-button.stories.tsx @@ -1,4 +1,4 @@ -import { Banner, FlowLayout, StackLayout } from "@salt-ds/core"; +import { Banner, Button, FlowLayout, StackLayout } from "@salt-ds/core"; import { CloseIcon, SearchIcon } from "@salt-ds/icons"; import { GhostButton, SystemStatus } from "@salt-ds/lab"; import type { Meta, StoryFn } from "@storybook/react"; @@ -16,6 +16,9 @@ export const TestingEverything: StoryFn = (props) => { Button + @@ -58,28 +61,33 @@ export const TestingEverything: StoryFn = (props) => {
- + Button
- - Button + + Search - - Button + + Search - - Button + + Search - - Button + + Search diff --git a/packages/lab/src/ghost-button/GhostButton.css b/packages/lab/src/ghost-button/GhostButton.css index c353b371eb..484b5362ac 100644 --- a/packages/lab/src/ghost-button/GhostButton.css +++ b/packages/lab/src/ghost-button/GhostButton.css @@ -1,12 +1,14 @@ /* TODO: sort and rename*/ .salt-theme.salt-theme-next[data-mode="light"] { + /* added the below token, needs renaming*/ --salt-palette-alpha-weaky: var(--salt-color-black-20a); --salt-palette-alpha-hover-alt: var(--salt-color-black-10a); --salt-palette-alpha-active-alt: var(--salt-color-black-20a); } .salt-theme.salt-theme-next[data-mode="dark"] { + /* added the below token, needs renaming*/ --salt-palette-alpha-weaky: var(--salt-color-white-20a); --salt-palette-alpha-hover-alt: var(--salt-color-black-10a); @@ -14,19 +16,24 @@ } .salt-theme[data-mode="light"] { + /* added the below token, needs renaming*/ --salt-palette-alpha-weaky: var(--salt-color-black-fade-background-selection); --salt-palette-alpha-weaker: var(--salt-color-black-fade-background-hover); + /* added alpha tokens, need renaming */ --salt-palette-alpha-hover-alt: var(--salt-color-black-fade-background-hover); --salt-palette-alpha-active-alt: var(--salt-color-black-fade-background-selection); } .salt-theme[data-mode="dark"] { + /* added the below token, needs renaming*/ --salt-palette-alpha-weaky: var(--salt-color-white-fade-background-selection); --salt-palette-alpha-weaker: var(--salt-color-white-fade-background-hover); + /* added alpha tokens, need renaming */ --salt-palette-alpha-hover-alt: var(--salt-color-black-fade-background-hover); --salt-palette-alpha-active-alt: var(--salt-color-black-fade-background-selection); } .salt-theme { + /*added 2 colors to current theme, not the same opacity but same would need another 2 tokens in opacity*/ --salt-color-white-fade-background-hover: rgba(255, 255, 255, var(--salt-opacity-8)); --salt-color-white-fade-background-selection: rgba(255, 255, 255, var(--salt-opacity-15)); } diff --git a/packages/theme/css/foundations/opacity.css b/packages/theme/css/foundations/opacity.css index 0c5cd0e6f4..3cbd27ec13 100644 --- a/packages/theme/css/foundations/opacity.css +++ b/packages/theme/css/foundations/opacity.css @@ -1,6 +1,7 @@ .salt-theme { --salt-opacity-0: 0; - --salt-opacity-8: 0.08; + /*--salt-opacity-8: 0.08; TODO: deprecate and add .1 and .2? */ + --salt-opacity-10: 0.1; --salt-opacity-15: 0.15; --salt-opacity-25: 0.25; --salt-opacity-40: 0.4;