From d6f70fb762fa2f78199cb2b32875924a802bb5d4 Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Mon, 17 Aug 2026 15:03:28 -0400 Subject: [PATCH 01/16] feat: promote PopoverContainer and SelectDropdown deep-import types to public API Part of the GMT-1740 exports-map migration. Promotes types from packages/gamut/src/PopoverContainer/types.ts and Form/SelectDropdown/types/* to the package root so consumers can stop reaching into internal dist/ paths. --- .../gamut/src/Form/SelectDropdown/index.tsx | 6 +--- .../src/PopoverContainer/PopoverContainer.tsx | 4 +-- .../__tests__/PopoverContainer.test.tsx | 6 ++-- packages/gamut/src/PopoverContainer/index.tsx | 1 + packages/gamut/src/PopoverContainer/types.ts | 28 ++++++------------- packages/gamut/src/PopoverContainer/utils.ts | 8 +++--- packages/gamut/src/index.tsx | 5 ---- 7 files changed, 19 insertions(+), 39 deletions(-) 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/PopoverContainer/PopoverContainer.tsx b/packages/gamut/src/PopoverContainer/PopoverContainer.tsx index c93f1965bc8..a1285992c2e 100644 --- a/packages/gamut/src/PopoverContainer/PopoverContainer.tsx +++ b/packages/gamut/src/PopoverContainer/PopoverContainer.tsx @@ -12,7 +12,7 @@ import { useScrollingParents, useScrollingParentsEffect, } from './hooks'; -import { ContainerState, PopoverContainerProps } from './types'; +import { PopoverContainerProps, PopoverContainerState } from './types'; import { getContainers, getPosition, isOutOfView } from './utils'; const PopoverContent = styled.div( @@ -46,7 +46,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 52c5439315e..4175949aae5 100644 --- a/packages/gamut/src/PopoverContainer/__tests__/PopoverContainer.test.tsx +++ b/packages/gamut/src/PopoverContainer/__tests__/PopoverContainer.test.tsx @@ -2,7 +2,7 @@ import { MockGamutProvider, setupRtl } from '@codecademy/gamut-tests'; import { cleanup, fireEvent, render, screen } from '@testing-library/react'; import { PopoverContainer } from '..'; -import { PopoverContainerProps, TargetRef } from '../types'; +import { PopoverContainerProps } from '../types'; import * as utils from '../utils'; import { createMockDOMRect, @@ -32,8 +32,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 8f958c921d0..41054446856 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' @@ -13,19 +13,7 @@ export type Alignments = | 'left' | 'right'; -export type TargetRef = Pick< - HTMLDivElement, - | 'getBoundingClientRect' - | 'contains' - | 'offsetHeight' - | 'offsetWidth' - | 'offsetTop' - | 'offsetLeft' - | 'offsetParent' -> & - HTMLElement; - -export interface PositionContext { +export interface PopoverPositionContext { width: number; height: number; top: number; @@ -34,8 +22,8 @@ export interface PositionContext { bottom: number; } -export interface ContainerState { - parent: PositionContext; +export interface PopoverContainerState { + parent: PopoverPositionContext; viewport: DOMRect; } @@ -43,7 +31,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 +51,8 @@ export interface PopoverAlignment { } export interface PopoverPositionConfig extends PopoverAlignment { - container: PositionContext; - alignment: Alignments; + container: PopoverPositionContext; + alignment: PopoverContainerAlignment; } export interface PopoverContainerProps @@ -84,7 +72,7 @@ export interface PopoverContainerProps /** * The target element around which the popover will be positioned. */ - targetRef: RefObject; + targetRef: RefObject; /** * If true, it will allow outside page interaction. Popover container will still close when clicking outside of the popover or hitting the escape key. */ 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/index.tsx b/packages/gamut/src/index.tsx index ef0e64206a2..89fc570c5cf 100644 --- a/packages/gamut/src/index.tsx +++ b/packages/gamut/src/index.tsx @@ -22,11 +22,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'; From bc2c49dfc32d33106f48d8f661dd270a32c793a4 Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Mon, 17 Aug 2026 15:31:43 -0400 Subject: [PATCH 02/16] form exports too --- packages/gamut/src/Form/index.tsx | 1 + packages/gamut/src/Form/types.ts | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) 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..7f6735732fa 100644 --- a/packages/gamut/src/Form/types.ts +++ b/packages/gamut/src/Form/types.ts @@ -2,8 +2,6 @@ export type FormValues = { [key in keyof T]?: T[key]; }; -export { CheckboxPaddingProps } from './inputs/Checkbox'; - export interface BaseInputProps { label?: string; error?: boolean; From a23735dcc48572c2e7a29b98eb85a9a1dcbc2ebc Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Mon, 17 Aug 2026 15:49:18 -0400 Subject: [PATCH 03/16] box --- packages/gamut/src/Box/GridBox.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/gamut/src/Box/GridBox.tsx b/packages/gamut/src/Box/GridBox.tsx index b005a768020..67d562fb57c 100644 --- a/packages/gamut/src/Box/GridBox.tsx +++ b/packages/gamut/src/Box/GridBox.tsx @@ -12,3 +12,5 @@ export const GridBox = styled( gridStates, boxProps ); + +export type { GridBoxProps } from './props'; From 2c760b4836c56e3dda61fbb36f2c848256d07e4f Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Mon, 17 Aug 2026 15:59:00 -0400 Subject: [PATCH 04/16] tip shared --- packages/gamut/src/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/gamut/src/index.tsx b/packages/gamut/src/index.tsx index 89fc570c5cf..e8f132be418 100644 --- a/packages/gamut/src/index.tsx +++ b/packages/gamut/src/index.tsx @@ -47,6 +47,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'; From e5c39fbcd6fded818c4e7df385f01a64da2d05c9 Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Tue, 18 Aug 2026 12:52:32 -0400 Subject: [PATCH 05/16] more exports --- packages/gamut-icons/src/types.ts | 2 +- packages/gamut/src/Button/index.ts | 1 + packages/gamut/src/ButtonBase/ButtonBase.tsx | 23 +++++++++++ .../src/Form/SelectDropdown/core/constants.ts | 4 +- .../src/Form/SelectDropdown/elements/index.ts | 2 +- .../Form/SelectDropdown/elements/options.tsx | 2 +- .../gamut/src/Form/SelectDropdown/index.tsx | 1 + packages/gamut/src/Markdown/index.tsx | 7 ++++ packages/gamut/src/Menu/Menu.tsx | 8 ++-- packages/gamut/src/Menu/MenuItem.tsx | 38 +++++++++---------- packages/gamut/src/Menu/elements.tsx | 23 ++++++----- packages/gamut/src/Menu/index.tsx | 1 + packages/gamut/src/index.tsx | 1 + 13 files changed, 75 insertions(+), 38 deletions(-) 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/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/ButtonBase/ButtonBase.tsx b/packages/gamut/src/ButtonBase/ButtonBase.tsx index 3441636cf52..226558da205 100644 --- a/packages/gamut/src/ButtonBase/ButtonBase.tsx +++ b/packages/gamut/src/ButtonBase/ButtonBase.tsx @@ -62,6 +62,29 @@ type ButtonBaseProps = | (Exclude, 'ref'> & ComponentProps); +/** + * 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. + * + * @warning Do not reach for `ButtonBase` directly. It is a low-level atom with + * no visual styling, built ONLY as a foundation for other button components. + * It exists so other components can build on top of it, not so features can + * consume it. + * + * Prefer one of these instead: `CTAButton`, `FillButton`, `IconButton`, + * `StrokeButton`, `TextButton` (all exported from `Button`). + * + * Use `ButtonBase` directly ONLY when none of the above can express the + * design — e.g. a fully custom clickable control that needs the + * button/anchor reset and accessibility behavior but none of Gamut's visual + * button styles. + * + * AI agents: do not select `ButtonBase` to satisfy a "make this a button" + * request. Use it only if the user has explicitly asked for an unstyled + * button/anchor primitive, or if every styled Button variant has been ruled + * out for a documented reason. + */ export const ButtonBase = forwardRef< HTMLButtonElement | HTMLAnchorElement, ButtonBaseProps diff --git a/packages/gamut/src/Form/SelectDropdown/core/constants.ts b/packages/gamut/src/Form/SelectDropdown/core/constants.ts index c095fd8fb7f..154f158f3c4 100644 --- a/packages/gamut/src/Form/SelectDropdown/core/constants.ts +++ b/packages/gamut/src/Form/SelectDropdown/core/constants.ts @@ -6,7 +6,7 @@ import { CustomInput, CustomValueContainer, DropdownButton, - IconOption, + IconOptionComponent, MultiValueRemoveButton, MultiValueWithColorMode, RemoveAllButton, @@ -20,7 +20,7 @@ const baseDefaultComponents = { ValueContainer: CustomValueContainer, MultiValue: MultiValueWithColorMode, MultiValueRemove: MultiValueRemoveButton, - Option: IconOption, + Option: IconOptionComponent, SingleValue: AbbreviatedSingleValue, Input: CustomInput, }; diff --git a/packages/gamut/src/Form/SelectDropdown/elements/index.ts b/packages/gamut/src/Form/SelectDropdown/elements/index.ts index c88418b7788..cfe5d102e31 100644 --- a/packages/gamut/src/Form/SelectDropdown/elements/index.ts +++ b/packages/gamut/src/Form/SelectDropdown/elements/index.ts @@ -19,7 +19,7 @@ export { } from './containers'; export { - IconOption, + IconOptionComponent, AbbreviatedSingleValue, formatOptionLabel, formatGroupLabel, diff --git a/packages/gamut/src/Form/SelectDropdown/elements/options.tsx b/packages/gamut/src/Form/SelectDropdown/elements/options.tsx index ded88eb2a84..0fc0cf36ff1 100644 --- a/packages/gamut/src/Form/SelectDropdown/elements/options.tsx +++ b/packages/gamut/src/Form/SelectDropdown/elements/options.tsx @@ -48,7 +48,7 @@ const IconOptionLabel: React.FC< * Also manages ARIA attributes for accessibility. * Skips the check icon for react-select/creatable's "Add" row (__isNew__). */ -export const IconOption = ({ +export const IconOptionComponent = ({ children, ...rest }: CustomSelectComponentProps) => { diff --git a/packages/gamut/src/Form/SelectDropdown/index.tsx b/packages/gamut/src/Form/SelectDropdown/index.tsx index 0fa359b90b1..94ed219bce5 100644 --- a/packages/gamut/src/Form/SelectDropdown/index.tsx +++ b/packages/gamut/src/Form/SelectDropdown/index.tsx @@ -1,2 +1,3 @@ export * from './types'; +export { IconOptionComponent } from './elements'; export * from './SelectDropdown'; 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/Menu/Menu.tsx b/packages/gamut/src/Menu/Menu.tsx index 279b4d19200..a3b7d8c48e7 100644 --- a/packages/gamut/src/Menu/Menu.tsx +++ b/packages/gamut/src/Menu/Menu.tsx @@ -1,11 +1,11 @@ import { ComponentProps, forwardRef } from 'react'; -import { List } from './elements'; +import { MenuList } from './elements'; import { MenuProvider, useMenu } from './MenuContext'; export const Menu = forwardRef< HTMLUListElement | HTMLOListElement, - Omit, 'root'> + Omit, 'root'> >( ( { children, variant = 'popover', spacing = 'normal', role, ...rest }, @@ -14,9 +14,9 @@ export const Menu = forwardRef< const currentContext = useMenu({ variant, role, spacing }); return ( - + {children} - + ); } ); diff --git a/packages/gamut/src/Menu/MenuItem.tsx b/packages/gamut/src/Menu/MenuItem.tsx index 8a74017b3ea..3e5495acdd4 100644 --- a/packages/gamut/src/Menu/MenuItem.tsx +++ b/packages/gamut/src/Menu/MenuItem.tsx @@ -12,11 +12,11 @@ import { FlexBox } from '../Box'; import { ToolTipProps } from '../Tip/ToolTip'; import { Text } from '../Typography'; import { - ListButton, - ListItem, - ListItemProps, - ListLink, - ListLinkProps, + MenuListButton, + MenuListItem, + MenuListItemProps, + MenuListLink, + MenuListLinkProps, MenuToolTipWrapper, } from './elements'; import { useMenuContext } from './MenuContext'; @@ -37,7 +37,7 @@ const currentItemText = { type HTMLProps = Partial>; type ForwardListItemProps = Omit< - ComponentProps, + ComponentProps, 'variant' | 'selected' | 'active-navlink' | 'children' >; @@ -96,7 +96,7 @@ export const MenuItem = forwardRef< role: listItemRole, height, width, - } as ListItemProps; + } as MenuListItemProps; const ariaLabel = label ? typeof label === 'string' @@ -140,18 +140,18 @@ export const MenuItem = forwardRef< const linkRef = ref as MutableRefObject; return ( - + - {content} - + - + ); } @@ -162,21 +162,21 @@ export const MenuItem = forwardRef< : (props.onClick as any as MouseEventHandler); return ( - + - {content} - + - + ); } @@ -184,9 +184,9 @@ export const MenuItem = forwardRef< return ( // These are non-interactive and will never have tooltips (nor should they). - + {content} - + ); } ); diff --git a/packages/gamut/src/Menu/elements.tsx b/packages/gamut/src/Menu/elements.tsx index 47aa6820117..16407b35d81 100644 --- a/packages/gamut/src/Menu/elements.tsx +++ b/packages/gamut/src/Menu/elements.tsx @@ -31,7 +31,7 @@ const listProps = variance.compose( system.color ); -export interface ListProps extends ListStyleProps, StyleStateProps { +export interface MenuListProps extends ListStyleProps, StyleStateProps { /** How offset spacing should be */ spacing?: 'normal' | 'condensed'; /** Menu variants for specific use cases and styles */ @@ -43,7 +43,7 @@ export interface ListProps extends ListStyleProps, StyleStateProps { showBorder?: boolean; } -const StyledList = styled('ul', styledOptions<'ul'>())( +const StyledList = styled('ul', styledOptions<'ul'>())( system.css({ listStyle: 'none', width: 1, @@ -67,7 +67,7 @@ const StyledList = styled('ul', styledOptions<'ul'>())( listProps ); -export const List = forwardRef< +export const MenuList = forwardRef< HTMLUListElement, ComponentProps >(({ context = true, m = 0, root = true, variant, ...rest }, ref) => ( @@ -192,14 +192,17 @@ const sizeVariants = system.variant({ }, }); -export interface ListItemProps +export interface MenuListItemProps extends ListStyleProps, StyleStateProps, StyleProps, StyleProps, StyleProps {} -export const ListItem = styled('li', styledOptions<'li'>())( +export const MenuListItem = styled( + 'li', + styledOptions<'li'>() +)( interactiveVariants, activeStates, sizeVariants, @@ -207,12 +210,12 @@ export const ListItem = styled('li', styledOptions<'li'>())( listProps ); -export interface ListLinkProps extends ListItemProps { +export interface MenuListLinkProps extends MenuListItemProps { active?: boolean; navlink?: boolean; } -const StyledListLink = styled('a', styledOptions<'a'>())( +const StyledListLink = styled('a', styledOptions<'a'>())( resetStyles, interactiveVariants, activeStates, @@ -221,17 +224,17 @@ const StyledListLink = styled('a', styledOptions<'a'>())( listProps ); -export const ListLink = forwardRef< +export const MenuListLink = forwardRef< HTMLAnchorElement, ComponentProps >(({ zIndex = 1, ...rest }, ref) => ( )); -export const ListButton = styled( +export const MenuListButton = styled( 'button', styledOptions<'button'>() -)( +)( resetStyles, interactiveVariants, activeStates, diff --git a/packages/gamut/src/Menu/index.tsx b/packages/gamut/src/Menu/index.tsx index 65077f603d9..54f17e79286 100644 --- a/packages/gamut/src/Menu/index.tsx +++ b/packages/gamut/src/Menu/index.tsx @@ -1,3 +1,4 @@ +export * from './elements'; export * from './Menu'; export * from './MenuItem'; export * from './MenuSeparator'; diff --git a/packages/gamut/src/index.tsx b/packages/gamut/src/index.tsx index e8f132be418..80d367a6933 100644 --- a/packages/gamut/src/index.tsx +++ b/packages/gamut/src/index.tsx @@ -8,6 +8,7 @@ export * from './BodyPortal'; export * from './Box'; export * from './Breadcrumbs'; export * from './Button'; +export { ButtonBase } from './ButtonBase'; export type { ButtonBaseElements } from './ButtonBase/ButtonBase'; export * from './Card'; export * from './Coachmark'; From 8d411077fbbfc12a186203e5e1a352cca6fd5e8f Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Tue, 25 Aug 2026 11:10:07 -0400 Subject: [PATCH 06/16] fix: restore CheckboxPaddingProps re-export from Form/types Dropped during the export-* switch, breaking GridForm/types.ts's import. --- packages/gamut/src/Form/types.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/gamut/src/Form/types.ts b/packages/gamut/src/Form/types.ts index 7f6735732fa..0298eacdcbe 100644 --- a/packages/gamut/src/Form/types.ts +++ b/packages/gamut/src/Form/types.ts @@ -2,6 +2,8 @@ export type FormValues = { [key in keyof T]?: T[key]; }; +export type { CheckboxPaddingProps } from './inputs/Checkbox'; + export interface BaseInputProps { label?: string; error?: boolean; From c3b18d9b1bd874c209b04e123a071ddd5ddf3e5e Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Tue, 25 Aug 2026 13:44:07 -0400 Subject: [PATCH 07/16] feat: rename ButtonSelectors to InteractiveSelectors and promote to public API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Renamed to match real usage (Anchor, Tag, Tip, Pagination — not just Button/ButtonBase), then exported from the root barrel. Documented alongside the other style utility helpers in Storybook. --- packages/gamut/src/Anchor/index.tsx | 20 +++++------ packages/gamut/src/Button/shared/styles.ts | 8 ++--- packages/gamut/src/Button/shared/variants.ts | 36 +++++++++---------- packages/gamut/src/ButtonBase/ButtonBase.tsx | 2 +- packages/gamut/src/Pagination/styles.tsx | 22 ++++++------ packages/gamut/src/Tag/styles.tsx | 20 +++++------ packages/gamut/src/Tip/InfoTip/styles.tsx | 12 +++---- .../gamut/src/Tip/PreviewTip/elements.tsx | 6 ++-- packages/gamut/src/index.tsx | 1 + .../lib/Foundations/Utilities/Utilities.mdx | 23 ++++++++++++ 10 files changed, 87 insertions(+), 63 deletions(-) diff --git a/packages/gamut/src/Anchor/index.tsx b/packages/gamut/src/Anchor/index.tsx index 287ff0e6d37..df113167bf1 100644 --- a/packages/gamut/src/Anchor/index.tsx +++ b/packages/gamut/src/Anchor/index.tsx @@ -3,7 +3,7 @@ import { StyleProps, variance } from '@codecademy/variance'; import styled from '@emotion/styled'; import { ComponentProps, forwardRef, HTMLProps, RefObject } from 'react'; -import { ButtonBase, ButtonSelectors } from '../ButtonBase/ButtonBase'; +import { ButtonBase, InteractiveSelectors } from '../ButtonBase/ButtonBase'; import { AppendedIconProps, appendIconToContent } from '../helpers'; export interface AnchorProps @@ -13,7 +13,7 @@ export interface AnchorProps } const outlineFocusVisible = { - [ButtonSelectors.OUTLINE]: { + [InteractiveSelectors.OUTLINE]: { content: "''", position: 'absolute', inset: -4, @@ -24,13 +24,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', @@ -49,11 +49,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', @@ -65,10 +65,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', @@ -84,10 +84,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/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 226558da205..57f7239abf7 100644 --- a/packages/gamut/src/ButtonBase/ButtonBase.tsx +++ b/packages/gamut/src/ButtonBase/ButtonBase.tsx @@ -15,7 +15,7 @@ export type ButtonBaseElementProps = HTMLProps< ref?: ButtonBaseRef; }; -export enum ButtonSelectors { +export enum InteractiveSelectors { HOVER = '&:hover', ACTIVE = '&:active', FOCUS = '&:focus', 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/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 80d367a6933..4de9e5e87fe 100644 --- a/packages/gamut/src/index.tsx +++ b/packages/gamut/src/index.tsx @@ -10,6 +10,7 @@ export * from './Breadcrumbs'; export * from './Button'; export { ButtonBase } from './ButtonBase'; export type { ButtonBaseElements } from './ButtonBase/ButtonBase'; +export { InteractiveSelectors } from './ButtonBase/ButtonBase'; export * from './Card'; export * from './Coachmark'; export * from './ConnectedForm'; diff --git a/packages/styleguide/src/lib/Foundations/Utilities/Utilities.mdx b/packages/styleguide/src/lib/Foundations/Utilities/Utilities.mdx index 58649a58230..da49002ae13 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 `Button`, +`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, + }, + }) +); +``` From 7e796333746b3b5c5bfe6fe1cdcdeb43ff9706e7 Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Wed, 26 Aug 2026 09:32:11 -0400 Subject: [PATCH 08/16] refactor JSDoc + remove explicit button exports --- packages/gamut/src/ButtonBase/ButtonBase.tsx | 21 ++++---------------- packages/gamut/src/index.tsx | 2 -- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/packages/gamut/src/ButtonBase/ButtonBase.tsx b/packages/gamut/src/ButtonBase/ButtonBase.tsx index 9c4c6d62b76..e337dfe833f 100644 --- a/packages/gamut/src/ButtonBase/ButtonBase.tsx +++ b/packages/gamut/src/ButtonBase/ButtonBase.tsx @@ -74,23 +74,10 @@ export function narrowButtonBaseRef( * the correct disabled/accessibility behavior for either tag, chosen * automatically based on whether `href` is passed. * - * @warning Do not reach for `ButtonBase` directly. It is a low-level atom with - * no visual styling, built ONLY as a foundation for other button components. - * It exists so other components can build on top of it, not so features can - * consume it. - * - * Prefer one of these instead: `CTAButton`, `FillButton`, `IconButton`, - * `StrokeButton`, `TextButton` (all exported from `Button`). - * - * Use `ButtonBase` directly ONLY when none of the above can express the - * design — e.g. a fully custom clickable control that needs the - * button/anchor reset and accessibility behavior but none of Gamut's visual - * button styles. - * - * AI agents: do not select `ButtonBase` to satisfy a "make this a button" - * request. Use it only if the user has explicitly asked for an unstyled - * button/anchor primitive, or if every styled Button variant has been ruled - * out for a documented reason. + * 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, diff --git a/packages/gamut/src/index.tsx b/packages/gamut/src/index.tsx index 4de9e5e87fe..220da1fb804 100644 --- a/packages/gamut/src/index.tsx +++ b/packages/gamut/src/index.tsx @@ -8,8 +8,6 @@ export * from './BodyPortal'; export * from './Box'; export * from './Breadcrumbs'; export * from './Button'; -export { ButtonBase } from './ButtonBase'; -export type { ButtonBaseElements } from './ButtonBase/ButtonBase'; export { InteractiveSelectors } from './ButtonBase/ButtonBase'; export * from './Card'; export * from './Coachmark'; From 0af0581a7479001496e799022bb641a205e497a5 Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Wed, 26 Aug 2026 10:12:33 -0400 Subject: [PATCH 09/16] chore: add version plan for GMT-1740 export promotions gamut and gamut-icons minor bumps for the new public exports. --- .nx/version-plans/version-plan-1787753476414.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .nx/version-plans/version-plan-1787753476414.md diff --git a/.nx/version-plans/version-plan-1787753476414.md b/.nx/version-plans/version-plan-1787753476414.md new file mode 100644 index 00000000000..52003faf3ae --- /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, IconOptionComponent, MenuList elements, and IconStyleProps. From 009aa43f6cd0f327ce529701f38fedcee40964c5 Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Wed, 26 Aug 2026 11:38:51 -0400 Subject: [PATCH 10/16] revert: move Menu/elements and IconOptionComponent renames to a separate PR Menu/elements' List->MenuList rename and Form/SelectDropdown's IconOption->IconOptionComponent rename are breaking changes layered on top of new exports; splitting them out for independent review. Only InteractiveSelectors (not yet public, so no compat surface) stays here. - Menu/elements: MenuList*/reverted to List*, and Menu/index.tsx no longer exports './elements' (would collide with the public List component). - Form/SelectDropdown: IconOptionComponent reverted to IconOption and no longer exported from the barrel; the ./types export (Rule 2, non-breaking) is untouched. --- .../version-plan-1787753476414.md | 2 +- .../src/Form/SelectDropdown/core/constants.ts | 4 +- .../src/Form/SelectDropdown/elements/index.ts | 2 +- .../Form/SelectDropdown/elements/options.tsx | 2 +- .../gamut/src/Form/SelectDropdown/index.tsx | 1 - packages/gamut/src/Menu/Menu.tsx | 8 ++-- packages/gamut/src/Menu/MenuItem.tsx | 40 +++++++++---------- packages/gamut/src/Menu/elements.tsx | 23 +++++------ packages/gamut/src/Menu/index.tsx | 1 - 9 files changed, 39 insertions(+), 44 deletions(-) diff --git a/.nx/version-plans/version-plan-1787753476414.md b/.nx/version-plans/version-plan-1787753476414.md index 52003faf3ae..b9020bd181a 100644 --- a/.nx/version-plans/version-plan-1787753476414.md +++ b/.nx/version-plans/version-plan-1787753476414.md @@ -3,4 +3,4 @@ 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, IconOptionComponent, MenuList elements, and IconStyleProps. +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/src/Form/SelectDropdown/core/constants.ts b/packages/gamut/src/Form/SelectDropdown/core/constants.ts index 154f158f3c4..c095fd8fb7f 100644 --- a/packages/gamut/src/Form/SelectDropdown/core/constants.ts +++ b/packages/gamut/src/Form/SelectDropdown/core/constants.ts @@ -6,7 +6,7 @@ import { CustomInput, CustomValueContainer, DropdownButton, - IconOptionComponent, + IconOption, MultiValueRemoveButton, MultiValueWithColorMode, RemoveAllButton, @@ -20,7 +20,7 @@ const baseDefaultComponents = { ValueContainer: CustomValueContainer, MultiValue: MultiValueWithColorMode, MultiValueRemove: MultiValueRemoveButton, - Option: IconOptionComponent, + Option: IconOption, SingleValue: AbbreviatedSingleValue, Input: CustomInput, }; diff --git a/packages/gamut/src/Form/SelectDropdown/elements/index.ts b/packages/gamut/src/Form/SelectDropdown/elements/index.ts index cfe5d102e31..c88418b7788 100644 --- a/packages/gamut/src/Form/SelectDropdown/elements/index.ts +++ b/packages/gamut/src/Form/SelectDropdown/elements/index.ts @@ -19,7 +19,7 @@ export { } from './containers'; export { - IconOptionComponent, + IconOption, AbbreviatedSingleValue, formatOptionLabel, formatGroupLabel, diff --git a/packages/gamut/src/Form/SelectDropdown/elements/options.tsx b/packages/gamut/src/Form/SelectDropdown/elements/options.tsx index 0fc0cf36ff1..ded88eb2a84 100644 --- a/packages/gamut/src/Form/SelectDropdown/elements/options.tsx +++ b/packages/gamut/src/Form/SelectDropdown/elements/options.tsx @@ -48,7 +48,7 @@ const IconOptionLabel: React.FC< * Also manages ARIA attributes for accessibility. * Skips the check icon for react-select/creatable's "Add" row (__isNew__). */ -export const IconOptionComponent = ({ +export const IconOption = ({ children, ...rest }: CustomSelectComponentProps) => { diff --git a/packages/gamut/src/Form/SelectDropdown/index.tsx b/packages/gamut/src/Form/SelectDropdown/index.tsx index 94ed219bce5..0fa359b90b1 100644 --- a/packages/gamut/src/Form/SelectDropdown/index.tsx +++ b/packages/gamut/src/Form/SelectDropdown/index.tsx @@ -1,3 +1,2 @@ export * from './types'; -export { IconOptionComponent } from './elements'; export * from './SelectDropdown'; diff --git a/packages/gamut/src/Menu/Menu.tsx b/packages/gamut/src/Menu/Menu.tsx index a3b7d8c48e7..279b4d19200 100644 --- a/packages/gamut/src/Menu/Menu.tsx +++ b/packages/gamut/src/Menu/Menu.tsx @@ -1,11 +1,11 @@ import { ComponentProps, forwardRef } from 'react'; -import { MenuList } from './elements'; +import { List } from './elements'; import { MenuProvider, useMenu } from './MenuContext'; export const Menu = forwardRef< HTMLUListElement | HTMLOListElement, - Omit, 'root'> + Omit, 'root'> >( ( { children, variant = 'popover', spacing = 'normal', role, ...rest }, @@ -14,9 +14,9 @@ export const Menu = forwardRef< const currentContext = useMenu({ variant, role, spacing }); return ( - + {children} - + ); } ); diff --git a/packages/gamut/src/Menu/MenuItem.tsx b/packages/gamut/src/Menu/MenuItem.tsx index 3402fdd4a96..add401014f4 100644 --- a/packages/gamut/src/Menu/MenuItem.tsx +++ b/packages/gamut/src/Menu/MenuItem.tsx @@ -12,11 +12,11 @@ import { FlexBox } from '../Box'; import { ToolTipProps } from '../Tip/ToolTip'; import { Text } from '../Typography'; import { - MenuListButton, - MenuListItem, - MenuListItemProps, - MenuListLink, - MenuListLinkProps, + ListButton, + ListItem, + ListItemProps, + ListLink, + ListLinkProps, MenuToolTipWrapper, } from './elements'; import { useMenuContext } from './MenuContext'; @@ -37,7 +37,7 @@ const currentItemText = { type HTMLProps = Partial>; type ForwardListItemProps = Omit< - ComponentProps, + ComponentProps, 'variant' | 'selected' | 'active-navlink' | 'children' >; @@ -108,7 +108,7 @@ export const MenuItem = forwardRef< role: listItemRole, height, width, - } as MenuListItemProps; + } as ListItemProps; const ariaLabel = label ? typeof label === 'string' @@ -150,18 +150,18 @@ export const MenuItem = forwardRef< if (listItemType === 'link' && !disabled) { return ( - + - (ref)} target={target} > {content} - + - + ); } @@ -171,32 +171,32 @@ export const MenuItem = forwardRef< : (props.onClick as any as MouseEventHandler); return ( - + - (ref)} onClick={handleClick} > {content} - + - + ); } return ( // These are non-interactive and will never have tooltips (nor should they). - (ref)} > {content} - + ); } ); diff --git a/packages/gamut/src/Menu/elements.tsx b/packages/gamut/src/Menu/elements.tsx index 16407b35d81..47aa6820117 100644 --- a/packages/gamut/src/Menu/elements.tsx +++ b/packages/gamut/src/Menu/elements.tsx @@ -31,7 +31,7 @@ const listProps = variance.compose( system.color ); -export interface MenuListProps extends ListStyleProps, StyleStateProps { +export interface ListProps extends ListStyleProps, StyleStateProps { /** How offset spacing should be */ spacing?: 'normal' | 'condensed'; /** Menu variants for specific use cases and styles */ @@ -43,7 +43,7 @@ export interface MenuListProps extends ListStyleProps, StyleStateProps { showBorder?: boolean; } -const StyledList = styled('ul', styledOptions<'ul'>())( +const StyledList = styled('ul', styledOptions<'ul'>())( system.css({ listStyle: 'none', width: 1, @@ -67,7 +67,7 @@ const StyledList = styled('ul', styledOptions<'ul'>())( listProps ); -export const MenuList = forwardRef< +export const List = forwardRef< HTMLUListElement, ComponentProps >(({ context = true, m = 0, root = true, variant, ...rest }, ref) => ( @@ -192,17 +192,14 @@ const sizeVariants = system.variant({ }, }); -export interface MenuListItemProps +export interface ListItemProps extends ListStyleProps, StyleStateProps, StyleProps, StyleProps, StyleProps {} -export const MenuListItem = styled( - 'li', - styledOptions<'li'>() -)( +export const ListItem = styled('li', styledOptions<'li'>())( interactiveVariants, activeStates, sizeVariants, @@ -210,12 +207,12 @@ export const MenuListItem = styled( listProps ); -export interface MenuListLinkProps extends MenuListItemProps { +export interface ListLinkProps extends ListItemProps { active?: boolean; navlink?: boolean; } -const StyledListLink = styled('a', styledOptions<'a'>())( +const StyledListLink = styled('a', styledOptions<'a'>())( resetStyles, interactiveVariants, activeStates, @@ -224,17 +221,17 @@ const StyledListLink = styled('a', styledOptions<'a'>())( listProps ); -export const MenuListLink = forwardRef< +export const ListLink = forwardRef< HTMLAnchorElement, ComponentProps >(({ zIndex = 1, ...rest }, ref) => ( )); -export const MenuListButton = styled( +export const ListButton = styled( 'button', styledOptions<'button'>() -)( +)( resetStyles, interactiveVariants, activeStates, diff --git a/packages/gamut/src/Menu/index.tsx b/packages/gamut/src/Menu/index.tsx index 54f17e79286..65077f603d9 100644 --- a/packages/gamut/src/Menu/index.tsx +++ b/packages/gamut/src/Menu/index.tsx @@ -1,4 +1,3 @@ -export * from './elements'; export * from './Menu'; export * from './MenuItem'; export * from './MenuSeparator'; From 2e94113d840c5440ff8c835fcd727dc77cf9288d Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Thu, 27 Aug 2026 10:18:21 -0400 Subject: [PATCH 11/16] tweak story --- packages/styleguide/src/lib/Foundations/Utilities/Utilities.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/styleguide/src/lib/Foundations/Utilities/Utilities.mdx b/packages/styleguide/src/lib/Foundations/Utilities/Utilities.mdx index da49002ae13..011fa2af4ad 100644 --- a/packages/styleguide/src/lib/Foundations/Utilities/Utilities.mdx +++ b/packages/styleguide/src/lib/Foundations/Utilities/Utilities.mdx @@ -109,7 +109,7 @@ const HiddenLabelWithFocus = ` `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 `Button`, +for outline/shadow effects). It is 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. From 00a0cbb104c0ffc01952534d588c82e2df021893 Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Thu, 27 Aug 2026 12:41:00 -0400 Subject: [PATCH 12/16] fix: restore ButtonBaseElements type export dropped by GMT-1740 cleanup 7e7963337 correctly un-exported the ButtonBase component but also deleted the ButtonBaseElements type export, which predates this branch and is a real public dependency (7 mono call sites use it for ref typing). Restore just the type export; ButtonBase itself stays unexported. --- packages/gamut/src/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/gamut/src/index.tsx b/packages/gamut/src/index.tsx index 220da1fb804..523f237b2aa 100644 --- a/packages/gamut/src/index.tsx +++ b/packages/gamut/src/index.tsx @@ -8,6 +8,7 @@ export * from './BodyPortal'; 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'; From b79135912c8cdf01db608e9c51e6f6c3e0c203d6 Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Thu, 27 Aug 2026 15:02:46 -0400 Subject: [PATCH 13/16] update export screenshop --- .../__snapshots__/gamut.test.ts.snap | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/gamut/__tests__/__snapshots__/gamut.test.ts.snap b/packages/gamut/__tests__/__snapshots__/gamut.test.ts.snap index 09329b96e55..e75b3cb54b0 100644 --- a/packages/gamut/__tests__/__snapshots__/gamut.test.ts.snap +++ b/packages/gamut/__tests__/__snapshots__/gamut.test.ts.snap @@ -8,9 +8,11 @@ exports[`Gamut Exported Keys 1`] = ` "AppWrapper", "Badge", "BarChart", + "BaseOnChangeProps", "BodyPortal", "Box", "Breadcrumbs", + "buttonProps", "Card", "Checkbox", "Coachmark", @@ -26,8 +28,14 @@ exports[`Gamut Exported Keys 1`] = ` "ConnectedSelect", "ConnectedTextArea", "ContentContainer", + "ControlState", + "createCodeBlockOverride", + "createInputOverride", "createPromise", + "createTagOverride", + "createVideoOverride", "CTAButton", + "CustomSelectComponentProps", "DataList", "DataTable", "DatePicker", @@ -40,6 +48,7 @@ exports[`Gamut Exported Keys 1`] = ` "Drawer", "ExpandControl", "ExpandInCollapseOut", + "ExtendedOption", "FadeInSlideOut", "FeatureShimmer", "FillButton", @@ -60,9 +69,11 @@ exports[`Gamut Exported Keys 1`] = ` "GridForm", "GridFormContent", "IconButton", + "IconOption", "iFrameWrapper", "InfoTip", "Input", + "InteractiveSelectors", "isClickableCrumb", "LayoutGrid", "List", @@ -74,7 +85,10 @@ exports[`Gamut Exported Keys 1`] = ` "MenuItem", "MenuSeparator", "Modal", + "MultiSelectDropdownProps", "omitProps", + "OptionState", + "OptionStrict", "Overlay", "Pagination", "Popover", @@ -89,10 +103,18 @@ exports[`Gamut Exported Keys 1`] = ` "Rotation", "Select", "SelectDropdown", + "SelectDropdownContextValueTypes", + "SelectDropdownGroup", + "SelectDropdownOptions", + "SelectDropdownProps", + "SelectDropdownSizes", "Shimmer", + "SingleSelectDropdownProps", + "SizedIndicatorProps", "SkipToContent", "SkipToContentTarget", "Spinner", + "standardOverrides", "StrokeButton", "SubmitButton", "submitSuccessStatus", @@ -108,10 +130,14 @@ exports[`Gamut Exported Keys 1`] = ` "Text", "TextArea", "TextButton", + "tipAlignmentArray", + "tipBaseAlignmentArray", + "tipDefaultProps", "Toast", "Toaster", "Toggle", "ToolTip", + "TypedReactSelectProps", "USE_DEBOUNCED_FIELD_DIRTY_KEY", "useConnectedForm", "useDatePicker", From 0b5c88db2c60e45a55b3bf06b904d1fdc81f3789 Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Thu, 27 Aug 2026 15:41:35 -0400 Subject: [PATCH 14/16] fix: use export type for SelectDropdown/types re-exports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Babel compiles each file independently and can't see across files that these re-exported names are type-only, so it emitted a runtime import for bindings that don't exist in the compiled output — breaking consumer webpack builds with "export 'X' was not found in './Y'". --- packages/gamut/src/Form/SelectDropdown/types/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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'; From 8a247eaac79fb4d6a4b6dba9cf6b301a726fdf5e Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Fri, 28 Aug 2026 08:43:15 -0400 Subject: [PATCH 15/16] tweak --- packages/styleguide/src/lib/Foundations/Utilities/Utilities.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/styleguide/src/lib/Foundations/Utilities/Utilities.mdx b/packages/styleguide/src/lib/Foundations/Utilities/Utilities.mdx index 011fa2af4ad..6113e819543 100644 --- a/packages/styleguide/src/lib/Foundations/Utilities/Utilities.mdx +++ b/packages/styleguide/src/lib/Foundations/Utilities/Utilities.mdx @@ -109,7 +109,7 @@ const HiddenLabelWithFocus = ` `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 is the same selector set that backs all `-Buttons`, +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. From 3f89af881d0b2a9c69602e7b298ca80d5297afcf Mon Sep 17 00:00:00 2001 From: dreamwasp Date: Fri, 28 Aug 2026 12:18:56 -0400 Subject: [PATCH 16/16] update snapshots --- .../__tests__/__snapshots__/gamut.test.ts.snap | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/packages/gamut/__tests__/__snapshots__/gamut.test.ts.snap b/packages/gamut/__tests__/__snapshots__/gamut.test.ts.snap index e75b3cb54b0..14d8d3f58a3 100644 --- a/packages/gamut/__tests__/__snapshots__/gamut.test.ts.snap +++ b/packages/gamut/__tests__/__snapshots__/gamut.test.ts.snap @@ -8,7 +8,6 @@ exports[`Gamut Exported Keys 1`] = ` "AppWrapper", "Badge", "BarChart", - "BaseOnChangeProps", "BodyPortal", "Box", "Breadcrumbs", @@ -28,14 +27,12 @@ exports[`Gamut Exported Keys 1`] = ` "ConnectedSelect", "ConnectedTextArea", "ContentContainer", - "ControlState", "createCodeBlockOverride", "createInputOverride", "createPromise", "createTagOverride", "createVideoOverride", "CTAButton", - "CustomSelectComponentProps", "DataList", "DataTable", "DatePicker", @@ -48,7 +45,6 @@ exports[`Gamut Exported Keys 1`] = ` "Drawer", "ExpandControl", "ExpandInCollapseOut", - "ExtendedOption", "FadeInSlideOut", "FeatureShimmer", "FillButton", @@ -69,7 +65,6 @@ exports[`Gamut Exported Keys 1`] = ` "GridForm", "GridFormContent", "IconButton", - "IconOption", "iFrameWrapper", "InfoTip", "Input", @@ -85,10 +80,7 @@ exports[`Gamut Exported Keys 1`] = ` "MenuItem", "MenuSeparator", "Modal", - "MultiSelectDropdownProps", "omitProps", - "OptionState", - "OptionStrict", "Overlay", "Pagination", "Popover", @@ -103,14 +95,7 @@ exports[`Gamut Exported Keys 1`] = ` "Rotation", "Select", "SelectDropdown", - "SelectDropdownContextValueTypes", - "SelectDropdownGroup", - "SelectDropdownOptions", - "SelectDropdownProps", - "SelectDropdownSizes", "Shimmer", - "SingleSelectDropdownProps", - "SizedIndicatorProps", "SkipToContent", "SkipToContentTarget", "Spinner", @@ -137,7 +122,6 @@ exports[`Gamut Exported Keys 1`] = ` "Toaster", "Toggle", "ToolTip", - "TypedReactSelectProps", "USE_DEBOUNCED_FIELD_DIRTY_KEY", "useConnectedForm", "useDatePicker",