diff --git a/.nx/version-plans/version-plan-1787753476414.md b/.nx/version-plans/version-plan-1787753476414.md new file mode 100644 index 00000000000..b9020bd181a --- /dev/null +++ b/.nx/version-plans/version-plan-1787753476414.md @@ -0,0 +1,6 @@ +--- +gamut-icons: minor +gamut: minor +--- + +Promote deep-import-only paths to public exports (GMT-1740): InteractiveSelectors, buttonProps, Tip/shared/types, Form/types, PopoverContainer/types, Markdown override factories, and IconStyleProps. The Menu/elements (MenuList\*) and IconOptionComponent renames are moved to a separate PR for independent review. diff --git a/packages/gamut-icons/src/types.ts b/packages/gamut-icons/src/types.ts index 8aaf152262f..9f1490b7945 100644 --- a/packages/gamut-icons/src/types.ts +++ b/packages/gamut-icons/src/types.ts @@ -1 +1 @@ -export type { GamutIconProps } from './props'; +export type { GamutIconProps, IconStyleProps } from './props'; diff --git a/packages/gamut/__tests__/__snapshots__/gamut.test.ts.snap b/packages/gamut/__tests__/__snapshots__/gamut.test.ts.snap index 09329b96e55..14d8d3f58a3 100644 --- a/packages/gamut/__tests__/__snapshots__/gamut.test.ts.snap +++ b/packages/gamut/__tests__/__snapshots__/gamut.test.ts.snap @@ -11,6 +11,7 @@ exports[`Gamut Exported Keys 1`] = ` "BodyPortal", "Box", "Breadcrumbs", + "buttonProps", "Card", "Checkbox", "Coachmark", @@ -26,7 +27,11 @@ exports[`Gamut Exported Keys 1`] = ` "ConnectedSelect", "ConnectedTextArea", "ContentContainer", + "createCodeBlockOverride", + "createInputOverride", "createPromise", + "createTagOverride", + "createVideoOverride", "CTAButton", "DataList", "DataTable", @@ -63,6 +68,7 @@ exports[`Gamut Exported Keys 1`] = ` "iFrameWrapper", "InfoTip", "Input", + "InteractiveSelectors", "isClickableCrumb", "LayoutGrid", "List", @@ -93,6 +99,7 @@ exports[`Gamut Exported Keys 1`] = ` "SkipToContent", "SkipToContentTarget", "Spinner", + "standardOverrides", "StrokeButton", "SubmitButton", "submitSuccessStatus", @@ -108,6 +115,9 @@ exports[`Gamut Exported Keys 1`] = ` "Text", "TextArea", "TextButton", + "tipAlignmentArray", + "tipBaseAlignmentArray", + "tipDefaultProps", "Toast", "Toaster", "Toggle", diff --git a/packages/gamut/src/Anchor/index.tsx b/packages/gamut/src/Anchor/index.tsx index 677074fac9a..6f8b3ad52df 100644 --- a/packages/gamut/src/Anchor/index.tsx +++ b/packages/gamut/src/Anchor/index.tsx @@ -11,7 +11,7 @@ import { import { ButtonBase, - ButtonSelectors, + InteractiveSelectors, narrowButtonBaseRef, } from '../ButtonBase/ButtonBase'; import { AppendedIconProps, appendIconToContent } from '../helpers'; @@ -23,7 +23,7 @@ export interface AnchorProps } const outlineFocusVisible = { - [ButtonSelectors.OUTLINE]: { + [InteractiveSelectors.OUTLINE]: { content: "''", position: 'absolute', inset: -4, @@ -34,13 +34,13 @@ const outlineFocusVisible = { zIndex: 0, }, - [ButtonSelectors.OUTLINE_FOCUS_VISIBLE]: { + [InteractiveSelectors.OUTLINE_FOCUS_VISIBLE]: { opacity: 1, }, } as const; const underlineFocusVisible = { - [ButtonSelectors.FOCUS_VISIBLE]: { + [InteractiveSelectors.FOCUS_VISIBLE]: { outline: 'currentColor solid 2px', borderRadius: 'sm', outlineOffset: '1.5px', @@ -59,11 +59,11 @@ const anchorVariants = variant({ position: 'relative', color: 'primary', whiteSpace: 'nowrap', - [ButtonSelectors.HOVER]: { + [InteractiveSelectors.HOVER]: { textDecoration: 'none', cursor: 'pointer', }, - [ButtonSelectors.DISABLED]: { + [InteractiveSelectors.DISABLED]: { cursor: 'not-allowed', textDecoration: 'none', color: 'text-disabled', @@ -75,10 +75,10 @@ const anchorVariants = variant({ fontWeight: 'bold', WebkitFontSmoothing: 'antialiased', MozOsxFontSmoothing: 'grayscale', - [ButtonSelectors.HOVER]: { + [InteractiveSelectors.HOVER]: { textDecoration: 'underline', }, - [ButtonSelectors.FOCUS_VISIBLE]: { + [InteractiveSelectors.FOCUS_VISIBLE]: { WebkitFontSmoothing: 'antialiased', MozOsxFontSmoothing: 'grayscale', outline: 'none', @@ -94,10 +94,10 @@ const anchorVariants = variant({ interface: { color: 'text', whiteSpace: 'initial', - [ButtonSelectors.HOVER]: { + [InteractiveSelectors.HOVER]: { color: 'primary', }, - [ButtonSelectors.FOCUS_VISIBLE]: { + [InteractiveSelectors.FOCUS_VISIBLE]: { color: 'primary', outline: 'none', }, diff --git a/packages/gamut/src/Button/index.ts b/packages/gamut/src/Button/index.ts index d9b61c232a4..dae5e26ff3e 100644 --- a/packages/gamut/src/Button/index.ts +++ b/packages/gamut/src/Button/index.ts @@ -1,4 +1,5 @@ export type { ButtonProps } from './shared'; +export { buttonProps } from './shared'; export * from './CTAButton'; export * from './FillButton'; export * from './IconButton'; diff --git a/packages/gamut/src/Button/shared/styles.ts b/packages/gamut/src/Button/shared/styles.ts index 9bcf0cbed8f..91087aa0eaf 100644 --- a/packages/gamut/src/Button/shared/styles.ts +++ b/packages/gamut/src/Button/shared/styles.ts @@ -8,7 +8,7 @@ import { import { CSSObject, ThemeProps, variance } from '@codecademy/variance'; import styled from '@emotion/styled'; -import { ButtonBase, ButtonSelectors } from '../../ButtonBase/ButtonBase'; +import { ButtonBase, InteractiveSelectors } from '../../ButtonBase/ButtonBase'; import { ButtonBaseProps } from './types'; export const config = styledOptions<'button', 'size'>(['size']); @@ -55,11 +55,11 @@ export const buttonStyles = system.css({ 'fast', 'ease-in' ), - [ButtonSelectors.DISABLED]: { + [InteractiveSelectors.DISABLED]: { cursor: 'not-allowed', userSelect: 'none', }, - [ButtonSelectors.OUTLINE]: { + [InteractiveSelectors.OUTLINE]: { content: '""', transition: transitionConcat(['opacity'], 'fast'), position: 'absolute', @@ -69,7 +69,7 @@ export const buttonStyles = system.css({ opacity: 0, zIndex: 0, }, - [ButtonSelectors.OUTLINE_FOCUS_VISIBLE]: { + [InteractiveSelectors.OUTLINE_FOCUS_VISIBLE]: { opacity: 1, }, }); diff --git a/packages/gamut/src/Button/shared/variants.ts b/packages/gamut/src/Button/shared/variants.ts index f99011970a4..eab5ecc78b5 100644 --- a/packages/gamut/src/Button/shared/variants.ts +++ b/packages/gamut/src/Button/shared/variants.ts @@ -1,6 +1,6 @@ import { theme, transitionConcat, variant } from '@codecademy/gamut-styles'; -import { ButtonSelectors } from '../../ButtonBase/ButtonBase'; +import { InteractiveSelectors } from '../../ButtonBase/ButtonBase'; import { buttonVariants, templateVariants } from './styles'; const hoverBackgroundTransition = transitionConcat( @@ -14,18 +14,18 @@ export const fillButtonVariants = templateVariants( (variant) => ({ bg: variant, color: 'background', - [ButtonSelectors.OUTLINE]: { borderColor: variant }, - [ButtonSelectors.HOVER]: { + [InteractiveSelectors.OUTLINE]: { borderColor: variant }, + [InteractiveSelectors.HOVER]: { bg: `${variant}-hover`, color: 'background', transition: hoverBackgroundTransition, }, - [ButtonSelectors.ACTIVE]: { + [InteractiveSelectors.ACTIVE]: { borderColor: 'border-primary', bg: variant, color: 'background', }, - [ButtonSelectors.DISABLED]: { + [InteractiveSelectors.DISABLED]: { color: 'text-disabled', bg: 'background-disabled', }, @@ -37,21 +37,21 @@ export const textButtonVariants = templateVariants( (variant) => ({ borderColor: 'transparent', color: variant === 'interface' ? 'text' : variant, - [ButtonSelectors.HOVER]: { + [InteractiveSelectors.HOVER]: { color: variant, bg: 'background-hover', transition: hoverBackgroundTransition, }, - [ButtonSelectors.FOCUS_VISIBLE]: { + [InteractiveSelectors.FOCUS_VISIBLE]: { color: variant, }, - [ButtonSelectors.OUTLINE]: { + [InteractiveSelectors.OUTLINE]: { borderColor: variant, }, - [ButtonSelectors.ACTIVE]: { + [InteractiveSelectors.ACTIVE]: { color: 'text', }, - [ButtonSelectors.DISABLED]: { + [InteractiveSelectors.DISABLED]: { color: 'text-disabled', bg: 'transparent', }, @@ -64,18 +64,18 @@ export const strokeButtonVariants = templateVariants( borderColor: variant, bg: 'transparent', color: variant, - [ButtonSelectors.HOVER]: { + [InteractiveSelectors.HOVER]: { bg: 'background-hover', transition: hoverBackgroundTransition, }, - [ButtonSelectors.OUTLINE]: { + [InteractiveSelectors.OUTLINE]: { borderColor: variant, }, - [ButtonSelectors.ACTIVE]: { + [InteractiveSelectors.ACTIVE]: { bg: variant, color: 'background', }, - [ButtonSelectors.DISABLED]: { + [InteractiveSelectors.DISABLED]: { borderColor: 'background-disabled', color: 'text-disabled', bg: 'transparent', @@ -92,21 +92,21 @@ export const ctaButtonVariants = templateVariants(['primary'], (variant) => ({ py: 12, px: 24, bg: variant, - [ButtonSelectors.OUTLINE]: { + [InteractiveSelectors.OUTLINE]: { borderColor: variant, bottom: -9, left: -9, }, - [ButtonSelectors.HOVER]: { + [InteractiveSelectors.HOVER]: { bg: `${variant}-hover`, transition: hoverBackgroundTransition, boxShadow: `-8px 8px 0 0 ${theme.colors.text}`, }, - [ButtonSelectors.ACTIVE]: { + [InteractiveSelectors.ACTIVE]: { boxShadow: 'none', bg: 'secondary', }, - [ButtonSelectors.DISABLED]: { + [InteractiveSelectors.DISABLED]: { boxShadow: 'none', color: 'text-disabled', bg: 'background-disabled', diff --git a/packages/gamut/src/ButtonBase/ButtonBase.tsx b/packages/gamut/src/ButtonBase/ButtonBase.tsx index 5c365befb3e..e337dfe833f 100644 --- a/packages/gamut/src/ButtonBase/ButtonBase.tsx +++ b/packages/gamut/src/ButtonBase/ButtonBase.tsx @@ -12,7 +12,7 @@ export type ButtonBaseElementProps = HTMLProps< ref?: ButtonBaseRef; }; -export enum ButtonSelectors { +export enum InteractiveSelectors { HOVER = '&:hover', ACTIVE = '&:active', FOCUS = '&:focus', @@ -69,6 +69,16 @@ export function narrowButtonBaseRef( return ref as Ref; } +/** + * An unstyled `button`/`a` element with the browser default styles reset and + * the correct disabled/accessibility behavior for either tag, chosen + * automatically based on whether `href` is passed. + * + * Not exported from the root barrel on purpose — it's a foundation other + * button components (`CTAButton`, `FillButton`, `IconButton`, `StrokeButton`, + * `TextButton`, `Anchor`) build on, not a public API surface. Only + * `InteractiveSelectors` is promoted from this module; see GMT-1740. + */ export const ButtonBase = forwardRef< HTMLButtonElement | HTMLAnchorElement, ButtonBaseProps diff --git a/packages/gamut/src/Form/SelectDropdown/index.tsx b/packages/gamut/src/Form/SelectDropdown/index.tsx index f2b4a387de8..0fa359b90b1 100644 --- a/packages/gamut/src/Form/SelectDropdown/index.tsx +++ b/packages/gamut/src/Form/SelectDropdown/index.tsx @@ -1,6 +1,2 @@ -export type { - OptionStrict, - ExtendedOption, - SelectDropdownGroup, -} from './types/options'; +export * from './types'; export * from './SelectDropdown'; diff --git a/packages/gamut/src/Form/SelectDropdown/types/index.ts b/packages/gamut/src/Form/SelectDropdown/types/index.ts index 537272372b3..9f32456a858 100644 --- a/packages/gamut/src/Form/SelectDropdown/types/index.ts +++ b/packages/gamut/src/Form/SelectDropdown/types/index.ts @@ -1,4 +1,4 @@ -export { +export type { OptionStrict, IconOption, ExtendedOption, @@ -6,7 +6,7 @@ export { SelectDropdownOptions, } from './options'; -export { +export type { SingleSelectDropdownProps, MultiSelectDropdownProps, SelectDropdownProps, @@ -14,10 +14,10 @@ export { TypedReactSelectProps, } from './component-props'; -export { +export type { SelectDropdownContextValueTypes, SizedIndicatorProps, CustomSelectComponentProps, } from './internal'; -export { ControlState, OptionState, SelectDropdownSizes } from './styles'; +export type { ControlState, OptionState, SelectDropdownSizes } from './styles'; diff --git a/packages/gamut/src/Form/index.tsx b/packages/gamut/src/Form/index.tsx index 95aa6f97110..f59e8507d74 100644 --- a/packages/gamut/src/Form/index.tsx +++ b/packages/gamut/src/Form/index.tsx @@ -12,3 +12,4 @@ export * from './inputs/TextArea'; export * from './inputs/Radio'; export * from './inputs/RadioGroup'; export * from './inputs/types'; +export * from './types'; diff --git a/packages/gamut/src/Form/types.ts b/packages/gamut/src/Form/types.ts index 1b8ee2a7075..0298eacdcbe 100644 --- a/packages/gamut/src/Form/types.ts +++ b/packages/gamut/src/Form/types.ts @@ -2,7 +2,7 @@ export type FormValues = { [key in keyof T]?: T[key]; }; -export { CheckboxPaddingProps } from './inputs/Checkbox'; +export type { CheckboxPaddingProps } from './inputs/Checkbox'; export interface BaseInputProps { label?: string; diff --git a/packages/gamut/src/Markdown/index.tsx b/packages/gamut/src/Markdown/index.tsx index 4bd8a3423d3..bd17f489d4b 100644 --- a/packages/gamut/src/Markdown/index.tsx +++ b/packages/gamut/src/Markdown/index.tsx @@ -196,3 +196,10 @@ export type { MarkdownOverrideSetting, MarkdownOverrideSettings, } from './libs/overrides'; +export { + createCodeBlockOverride, + createInputOverride, + createTagOverride, + createVideoOverride, + standardOverrides, +} from './libs/overrides'; diff --git a/packages/gamut/src/Pagination/styles.tsx b/packages/gamut/src/Pagination/styles.tsx index b9f8177cb19..6f67b2a49f0 100644 --- a/packages/gamut/src/Pagination/styles.tsx +++ b/packages/gamut/src/Pagination/styles.tsx @@ -1,7 +1,7 @@ import { states, theme, transitionConcat } from '@codecademy/gamut-styles'; import { templateVariants } from '../Button/shared/styles'; -import { ButtonSelectors } from '../ButtonBase/ButtonBase'; +import { InteractiveSelectors } from '../ButtonBase/ButtonBase'; const paginationBaseStyles = { color: 'text', @@ -9,31 +9,31 @@ const paginationBaseStyles = { height: 40, mx: 4, width: 40, - [ButtonSelectors.ACTIVE]: { + [InteractiveSelectors.ACTIVE]: { fontWeight: 'title', color: 'text', }, - [ButtonSelectors.DISABLED]: { + [InteractiveSelectors.DISABLED]: { color: 'text-disabled', bg: 'transparent', }, - [ButtonSelectors.OUTLINE]: { borderColor: 'border-primary' }, - [ButtonSelectors.SHADOW_ACTIVE]: { opacity: 0 }, - [ButtonSelectors.SHADOW_HOVER]: { opacity: 0 }, + [InteractiveSelectors.OUTLINE]: { borderColor: 'border-primary' }, + [InteractiveSelectors.SHADOW_ACTIVE]: { opacity: 0 }, + [InteractiveSelectors.SHADOW_HOVER]: { opacity: 0 }, } as const; export const paginationTextVariant = templateVariants(['secondary'], () => ({ ...paginationBaseStyles, borderColor: 'transparent', - [ButtonSelectors.ACTIVE]: { + [InteractiveSelectors.ACTIVE]: { fontWeight: 'title', color: 'text', bg: 'background-selected', }, - [ButtonSelectors.SHADOW]: { + [InteractiveSelectors.SHADOW]: { transition: transitionConcat(['opacity'], 'fast', 'ease-in'), }, - [ButtonSelectors.HOVER]: { + [InteractiveSelectors.HOVER]: { transition: transitionConcat( ['background-color', 'font-weight'], 'fast', @@ -55,10 +55,10 @@ export const paginationTextButtonStates = states({ export const paginationStrokeVariant = templateVariants(['secondary'], () => ({ ...paginationBaseStyles, - [ButtonSelectors.ACTIVE]: { + [InteractiveSelectors.ACTIVE]: { borderColor: 'currentColor', }, - [ButtonSelectors.SHADOW]: { + [InteractiveSelectors.SHADOW]: { transition: transitionConcat( ['opacity', 'border-color'], 'fast', diff --git a/packages/gamut/src/PopoverContainer/PopoverContainer.tsx b/packages/gamut/src/PopoverContainer/PopoverContainer.tsx index 9b1ce631a3b..b259b8baef6 100644 --- a/packages/gamut/src/PopoverContainer/PopoverContainer.tsx +++ b/packages/gamut/src/PopoverContainer/PopoverContainer.tsx @@ -14,7 +14,11 @@ import { useScrollingParents, useScrollingParentsEffect, } from './hooks'; -import { ContainerState, PopoverContainerProps, TargetRef } from './types'; +import { + PopoverContainerProps, + PopoverContainerState, + TargetRef, +} from './types'; import { getContainers, getPosition, isOutOfView } from './utils'; const PopoverContent = styled.div( @@ -48,7 +52,7 @@ export const PopoverContainer: React.FC = ({ const onRequestCloseRef = useRef(onRequestClose); const { width: winW, height: winH } = useWindowSize(); const { x: winX, y: winY } = useWindowScroll(); - const [containers, setContainers] = useState(); + const [containers, setContainers] = useState(); const [targetRect, setTargetRect] = useState(); const parent = containers?.parent; diff --git a/packages/gamut/src/PopoverContainer/__tests__/PopoverContainer.test.tsx b/packages/gamut/src/PopoverContainer/__tests__/PopoverContainer.test.tsx index 47203b9be71..ba2775868db 100644 --- a/packages/gamut/src/PopoverContainer/__tests__/PopoverContainer.test.tsx +++ b/packages/gamut/src/PopoverContainer/__tests__/PopoverContainer.test.tsx @@ -3,7 +3,7 @@ import { cleanup, fireEvent, render, screen } from '@testing-library/react'; import React from 'react'; import { PopoverContainer } from '..'; -import { PopoverContainerProps, TargetRef } from '../types'; +import { PopoverContainerProps } from '../types'; import * as utils from '../utils'; import { createMockDOMRect, @@ -33,8 +33,8 @@ const defaultTarget = { }; const mockTargetRef = ( - target?: Partial, - viewport?: Partial> + target?: Partial, + viewport?: Partial> ) => ({ current: { diff --git a/packages/gamut/src/PopoverContainer/index.tsx b/packages/gamut/src/PopoverContainer/index.tsx index a38dfe47e19..2c382805443 100644 --- a/packages/gamut/src/PopoverContainer/index.tsx +++ b/packages/gamut/src/PopoverContainer/index.tsx @@ -1 +1,2 @@ export * from './PopoverContainer'; +export * from './types'; diff --git a/packages/gamut/src/PopoverContainer/types.ts b/packages/gamut/src/PopoverContainer/types.ts index 94a6744b620..d70994aca81 100644 --- a/packages/gamut/src/PopoverContainer/types.ts +++ b/packages/gamut/src/PopoverContainer/types.ts @@ -3,7 +3,7 @@ import { RefObject } from 'react'; import { FocusTrapProps } from '../FocusTrap'; import { WithChildrenProp } from '../utils'; -export type Alignments = +export type PopoverContainerAlignment = | 'top-left' | 'top-right' | 'bottom-left' @@ -25,7 +25,7 @@ export type TargetRef = Pick< > & HTMLElement; -export interface PositionContext { +export interface PopoverPositionContext { width: number; height: number; top: number; @@ -34,8 +34,8 @@ export interface PositionContext { bottom: number; } -export interface ContainerState { - parent: PositionContext; +export interface PopoverContainerState { + parent: PopoverPositionContext; viewport: DOMRect; } @@ -43,7 +43,7 @@ export interface PopoverAlignment { /** * Which vertical edge of the source component to align against. */ - alignment?: Alignments; + alignment?: PopoverContainerAlignment; /** Align to the inside edge of the target div */ invertAxis?: 'x' | 'y'; /** Whether the popover renders inside the current DOM context or escapes with a portal */ @@ -63,8 +63,8 @@ export interface PopoverAlignment { } export interface PopoverPositionConfig extends PopoverAlignment { - container: PositionContext; - alignment: Alignments; + container: PopoverPositionContext; + alignment: PopoverContainerAlignment; } export interface PopoverContainerProps diff --git a/packages/gamut/src/PopoverContainer/utils.ts b/packages/gamut/src/PopoverContainer/utils.ts index 42a7c5e93e6..fb74f654d9a 100644 --- a/packages/gamut/src/PopoverContainer/utils.ts +++ b/packages/gamut/src/PopoverContainer/utils.ts @@ -3,7 +3,7 @@ import { percentageOrAbsolute as percent, } from '@codecademy/variance'; -import { Alignments, PopoverPositionConfig, TargetRef } from './types'; +import { PopoverContainerAlignment, PopoverPositionConfig } from './types'; export interface PopoverPositionResult { /** Edge insets pinning the popover to the container (`left` / `right` / `top` / `bottom`). */ @@ -20,9 +20,9 @@ export interface PopoverPositionResult { * uses the same geometry as `bottom-right` in LTR. */ export const mirrorAlignment = ( - alignment: Alignments, + alignment: PopoverContainerAlignment, isRtl: boolean -): Alignments => { +): PopoverContainerAlignment => { if (!isRtl) return alignment; switch (alignment) { case 'top-left': @@ -279,7 +279,7 @@ export const getPosition = ({ }; export const getContainers = ( - target: TargetRef, + target: HTMLElement, inline = false, scroll: { x: number; y: number } ) => { diff --git a/packages/gamut/src/Tag/styles.tsx b/packages/gamut/src/Tag/styles.tsx index f56ed207147..0cd8eb98d67 100644 --- a/packages/gamut/src/Tag/styles.tsx +++ b/packages/gamut/src/Tag/styles.tsx @@ -1,6 +1,6 @@ import { css, states, theme, variant } from '@codecademy/gamut-styles'; -import { ButtonSelectors, Selectors } from '../ButtonBase/ButtonBase'; +import { InteractiveSelectors, Selectors } from '../ButtonBase/ButtonBase'; export const tagLabelFontSize = 14; export const tagLabelPadding = 8; @@ -76,7 +76,7 @@ export const tagUsageVariants = variant({ bg: 'background-current', border: 1, borderColor: 'border-secondary', - [ButtonSelectors.ACTIVE]: { + [InteractiveSelectors.ACTIVE]: { bg: 'secondary', }, }, @@ -148,31 +148,31 @@ export const anchorVariants = variant({ display: 'none', }, color: 'text', - [ButtonSelectors.FOCUS_VISIBLE]: { + [InteractiveSelectors.FOCUS_VISIBLE]: { outline: 'none', border: 'none', }, - [ButtonSelectors.FOCUS]: { + [InteractiveSelectors.FOCUS]: { textDecoration: 'none', }, - [ButtonSelectors.HOVER]: { + [InteractiveSelectors.HOVER]: { bg: 'background-hover', }, - [ButtonSelectors.DISABLED]: { + [InteractiveSelectors.DISABLED]: { borderColor: 'border-disabled', bg: 'background-current', }, }, variants: { navigation: { - [ButtonSelectors.ACTIVE]: { + [InteractiveSelectors.ACTIVE]: { color: 'background', bg: 'secondary', textDecoration: 'none', }, }, suggestion: { - [ButtonSelectors.ACTIVE]: { + [InteractiveSelectors.ACTIVE]: { bg: 'primary', color: 'background', textDecoration: 'none', @@ -187,7 +187,7 @@ export const dismissButtonOverrides = { display: 'none', }, // This removes a black solid outline on focus - [ButtonSelectors.OUTLINE_FOCUS_VISIBLE]: { + [InteractiveSelectors.OUTLINE_FOCUS_VISIBLE]: { opacity: 0, }, }; @@ -209,7 +209,7 @@ export const dismissButtonStyling = css({ color: 'background', bg: 'secondary-hover', }, - [ButtonSelectors.DISABLED]: { + [InteractiveSelectors.DISABLED]: { bg: 'background-disabled', color: 'text-disabled', }, diff --git a/packages/gamut/src/Tip/InfoTip/styles.tsx b/packages/gamut/src/Tip/InfoTip/styles.tsx index 9d5a8953bcb..0521c2500af 100644 --- a/packages/gamut/src/Tip/InfoTip/styles.tsx +++ b/packages/gamut/src/Tip/InfoTip/styles.tsx @@ -9,7 +9,7 @@ import { StyleProps } from '@codecademy/variance'; import styled from '@emotion/styled'; import { Box } from '../../Box'; -import { ButtonSelectors } from '../../ButtonBase/ButtonBase'; +import { InteractiveSelectors } from '../../ButtonBase/ButtonBase'; import { tooltipArrowHeight, tooltipBgColor, @@ -24,20 +24,20 @@ export const infoButtonStyles = css({ color: textColor, height: 24, width: 24, - [ButtonSelectors.HOVER]: { + [InteractiveSelectors.HOVER]: { color: textColor, bg: 'background-hover', }, - [ButtonSelectors.FOCUS_VISIBLE]: { + [InteractiveSelectors.FOCUS_VISIBLE]: { color: textColor, }, - [ButtonSelectors.OUTLINE]: { + [InteractiveSelectors.OUTLINE]: { borderColor: textColor, }, - [ButtonSelectors.ACTIVE]: { + [InteractiveSelectors.ACTIVE]: { color: 'text', }, - [ButtonSelectors.DISABLED]: { + [InteractiveSelectors.DISABLED]: { color: 'text-disabled', bg: 'transparent', }, diff --git a/packages/gamut/src/Tip/PreviewTip/elements.tsx b/packages/gamut/src/Tip/PreviewTip/elements.tsx index 6f42308c3c4..7a636861651 100644 --- a/packages/gamut/src/Tip/PreviewTip/elements.tsx +++ b/packages/gamut/src/Tip/PreviewTip/elements.tsx @@ -5,7 +5,7 @@ import { useMemo } from 'react'; import { Anchor } from '../../Anchor'; import { Box, FlexBox, GridBox } from '../../Box'; -import { ButtonSelectors } from '../../ButtonBase/ButtonBase'; +import { InteractiveSelectors } from '../../ButtonBase/ButtonBase'; import { Shimmer } from '../../Loading/Shimmer'; import { patternContainerBaseStyles } from '../../Popover/styles/pattern'; import { Text } from '../../Typography'; @@ -24,14 +24,14 @@ export const PreviewTipAnchor = styled(Anchor)( variants: { anchor: { textDecorationStyle: 'dotted', - [ButtonSelectors.HOVER]: { + [InteractiveSelectors.HOVER]: { textDecoration: 'underline', textDecorationStyle: 'solid', }, }, avatar: { borderRadius: 'sm', - [ButtonSelectors.HOVER]: { + [InteractiveSelectors.HOVER]: { bg: 'background-hover', }, }, diff --git a/packages/gamut/src/index.tsx b/packages/gamut/src/index.tsx index ef0e64206a2..523f237b2aa 100644 --- a/packages/gamut/src/index.tsx +++ b/packages/gamut/src/index.tsx @@ -9,6 +9,7 @@ export * from './Box'; export * from './Breadcrumbs'; export * from './Button'; export type { ButtonBaseElements } from './ButtonBase/ButtonBase'; +export { InteractiveSelectors } from './ButtonBase/ButtonBase'; export * from './Card'; export * from './Coachmark'; export * from './ConnectedForm'; @@ -22,11 +23,6 @@ export * from './FeatureShimmer'; export * from './Flyout'; export * from './FocusTrap'; export * from './Form'; -export type { - OptionStrict, - ExtendedOption, - SelectDropdownGroup, -} from './Form/SelectDropdown'; export * from './GridForm'; export * from './Layout/Column'; export * from './Layout/LayoutGrid'; @@ -52,6 +48,7 @@ export * from './Toggle'; export * from './Tip/InfoTip'; export * from './Tip/PreviewTip'; export * from './Tip/ToolTip'; +export * from './Tip/shared/types'; export * from './typings/responsive-properties'; export * from './Typography/Text'; export type { HeadingTags } from './Typography/types'; diff --git a/packages/styleguide/src/lib/Foundations/Utilities/Utilities.mdx b/packages/styleguide/src/lib/Foundations/Utilities/Utilities.mdx index 58649a58230..6113e819543 100644 --- a/packages/styleguide/src/lib/Foundations/Utilities/Utilities.mdx +++ b/packages/styleguide/src/lib/Foundations/Utilities/Utilities.mdx @@ -104,3 +104,26 @@ const HiddenLabelWithFocus = ` ${screenReaderOnlyFocusable} `; ``` + +### Interactive state selectors + +`InteractiveSelectors` is the shared vocabulary of CSS selectors for interactive +element states (hover, active, focus, disabled, and the pseudo-elements Gamut uses +for outline/shadow effects). It's the same selector set that backs all `-Buttons`, +`Anchor`, `Tag`, the `Tip` family, and `Pagination` internally — reach for it when +styling a custom interactive element that needs to match those conventions. + +```tsx +import { InteractiveSelectors } from '@codecademy/gamut'; + +const CustomControl = styled.button( + css({ + [InteractiveSelectors.HOVER]: { + textColor: 'primary', + }, + [InteractiveSelectors.DISABLED]: { + opacity: 0.5, + }, + }) +); +```