Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Token): Migrate to CSS modules behind feature flag Pt 1 #5251

Merged
merged 10 commits into from
Nov 12, 2024
50 changes: 50 additions & 0 deletions packages/react/src/Token/_RemoveTokenButton.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
.TokenButton {
display: inline-flex;
padding: 0;
margin-left: var(--base-size-4);
font-family: inherit;
color: currentColor;
text-decoration: none;
cursor: pointer;
user-select: none;
background-color: transparent;
border: 0;
border-radius: var(--borderRadius-full);
justify-content: center;
align-items: center;
appearance: none;
align-self: baseline;
}

.TokenButton[data-size='small'] {
width: var(--base-size-16);
height: var(--base-size-16);
}

.TokenButton[data-size='medium'] {
width: 20px;
height: 20px;
}

.TokenButton[data-size='large'] {
width: var(--base-size-24);
height: var(--base-size-24);
margin-left: var(--base-size-8);
}

.TokenButton[data-size='xlarge'] {
width: var(--base-size-32);
height: var(--base-size-32);
margin-left: var(--base-size-8);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious could you add this to the .TokenButton[data-size='xlarge'] and .TokenButton[data-size='large'] selectors instead of adding .Bigger?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good callout! Update

}

.TokenButton:hover,
.TokenButton:focus {
/* TODO: choose a better functional color variable for this */
background-color: var(--bgColor-neutral-muted);
}

.TokenButton:active {
/* TODO: choose a better functional color variable for this */
background-color: var(--bgColor-neutral-muted);
}
115 changes: 69 additions & 46 deletions packages/react/src/Token/_RemoveTokenButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,24 @@ import React from 'react'
import {XIcon} from '@primer/octicons-react'
import styled, {css} from 'styled-components'
import {variant} from 'styled-system'
import {clsx} from 'clsx'
import {get} from '../constants'
import type {SxProp} from '../sx'
import sx from '../sx'
import type {ComponentProps} from '../utils/types'
import sx, {type SxProp} from '../sx'
import type {TokenSizeKeys} from './TokenBase'
import {tokenSizes, defaultTokenSize} from './TokenBase'
import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent'
import {useFeatureFlag} from '../FeatureFlags'

interface TokenButtonProps {
import classes from './_RemoveTokenButton.module.css'

interface TokenButtonProps extends SxProp {
borderOffset?: number
size?: TokenSizeKeys
isParentInteractive?: boolean
}

const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_team'

const variants = variant<{height: string; width: string}, TokenSizeKeys>({
prop: 'size',
variants: {
Expand All @@ -39,66 +44,84 @@ const variants = variant<{height: string; width: string}, TokenSizeKeys>({

const getTokenButtonIconSize = (size?: TokenSizeKeys) => parseInt(tokenSizes[size || defaultTokenSize], 10) * 0.75

const StyledTokenButton = styled.span<TokenButtonProps & SxProp>`
background-color: transparent;
font-family: inherit;
color: currentColor;
cursor: pointer;
display: inline-flex;
justify-content: center;
align-items: center;
user-select: none;
appearance: none;
text-decoration: none;
padding: 0;
transform: ${props => `translate(${props.borderOffset}px, -${props.borderOffset}px)`};
align-self: baseline;
border: 0;
border-radius: 999px;
const StyledTokenButton = toggleStyledComponent(
CSS_MODULES_FEATURE_FLAG,
'span',
styled.span<TokenButtonProps>`
background-color: transparent;
font-family: inherit;
color: currentColor;
cursor: pointer;
display: inline-flex;
justify-content: center;
align-items: center;
user-select: none;
appearance: none;
text-decoration: none;
padding: 0;
transform: ${props => `translate(${props.borderOffset}px, -${props.borderOffset}px)`};
align-self: baseline;
border: 0;
border-radius: 999px;

${props => {
switch (props.size) {
case 'large':
case 'xlarge':
return css`
margin-left: ${get('space.2')};
`
default:
return css`
margin-left: ${get('space.1')};
`
}
}}

${props => {
switch (props.size) {
case 'large':
case 'xlarge':
return css`
margin-left: ${get('space.2')};
`
default:
return css`
margin-left: ${get('space.1')};
`
&:hover,
&:focus {
// TODO: choose a better functional color variable for this
background-color: ${get('colors.neutral.muted')};
}
}}

&:hover,
&:focus {
// TODO: choose a better functional color variable for this
background-color: ${get('colors.neutral.muted')};
}
&:active {
// TODO: choose a better functional color variable for this
background-color: ${get('colors.neutral.subtle')};
}

&:active {
// TODO: choose a better functional color variable for this
background-color: ${get('colors.neutral.subtle')};
}
${variants}
${sx}
`,
)

${variants}
${sx}
`
type RemoveTokenButtonProps = TokenButtonProps & Omit<React.HTMLProps<HTMLSpanElement | HTMLButtonElement>, 'size'>

const RemoveTokenButton: React.FC<React.PropsWithChildren<ComponentProps<typeof StyledTokenButton>>> = ({
const RemoveTokenButton = ({
'aria-label': ariaLabel,
isParentInteractive,
size = defaultTokenSize,
className,
...rest
}) => {
}: React.PropsWithChildren<RemoveTokenButtonProps>) => {
delete rest.children

const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)

return (
<StyledTokenButton
as={isParentInteractive ? 'span' : 'button'}
tabIndex={isParentInteractive ? -1 : undefined}
aria-label={!isParentInteractive ? 'Remove token' : ariaLabel}
size={size}
data-size={size}
className={clsx(enabled && classes.TokenButton, className)}
style={
enabled
? {
transform: `translate(${rest.borderOffset}px, -${rest.borderOffset}px)`,
}
: {}
}
{...rest}
>
<XIcon size={getTokenButtonIconSize(size)} />
Expand Down
46 changes: 46 additions & 0 deletions packages/react/src/Token/_TokenTextContainer.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.TokenTextContainer {
width: auto;
min-width: 0;
padding: 0;
margin: 0;
overflow: hidden;
font: inherit;
/* stylelint-disable-next-line primer/typography */
line-height: normal;
color: inherit;

/* reset anchor styles */
color: currentColor;
text-decoration: none;
text-overflow: ellipsis;
white-space: nowrap;

/* reset button styles, make the cursor a pointer, and add line-height */
background: transparent;
border: none;
flex-grow: 1;
-webkit-font-smoothing: inherit;
-moz-osx-font-smoothing: inherit;
appearance: none;
}

/* Position psuedo-element above text content, but below the
remove button.
This ensures the <a> or <button> receives the click no
matter where on the token the user clicks. */
.TokenTextContainer:is(a, button, [tabIndex='0']) {
cursor: pointer;
}

/* Position psuedo-element above text content, but below the
remove button.
This ensures the <a> or <button> receives the click no
matter where on the token the user clicks. */
.TokenTextContainer:is(a, button, [tabIndex='0'])::after {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
content: '';
}
101 changes: 61 additions & 40 deletions packages/react/src/Token/_TokenTextContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,67 @@
import styled from 'styled-components'
import type {TokenBaseProps} from './TokenBase'
import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent'
import React from 'react'
import classes from './_TokenTextContainer.module.css'
import {useFeatureFlag} from '../FeatureFlags'
import {clsx} from 'clsx'

const TokenTextContainer = styled('span')<Partial<TokenBaseProps>>`
flex-grow: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;

// reset button styles, make the cursor a pointer, and add line-height
background: transparent;
border: none;
color: inherit;
font: inherit;
margin: 0;
padding: 0;
width: auto;
-webkit-font-smoothing: inherit;
-moz-osx-font-smoothing: inherit;
-webkit-appearance: none;
line-height: normal;

// reset anchor styles
color: currentColor;
text-decoration: none;

// Position psuedo-element above text content, but below the
// remove button.
// This ensures the <a> or <button> receives the click no
// matter where on the token the user clicks.
&:is(a, button, [tabIndex='0']) {
cursor: pointer;

&:after {
content: '';
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_team'

const StyledTokenTextContainer = toggleStyledComponent(
CSS_MODULES_FEATURE_FLAG,
'span',
styled('span')`
flex-grow: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;

// reset button styles, make the cursor a pointer, and add line-height
background: transparent;
border: none;
color: inherit;
font: inherit;
margin: 0;
padding: 0;
width: auto;
-webkit-font-smoothing: inherit;
-moz-osx-font-smoothing: inherit;
-webkit-appearance: none;
line-height: normal;

// reset anchor styles
color: currentColor;
text-decoration: none;

// Position psuedo-element above text content, but below the
// remove button.
// This ensures the <a> or <button> receives the click no
// matter where on the token the user clicks.
&:is(a, button, [tabIndex='0']) {
cursor: pointer;

&:after {
content: '';
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
}
}
`
`,
)

const TokenTextContainer = ({children, ...props}: React.PropsWithChildren<Partial<TokenBaseProps>>) => {
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)

return (
<StyledTokenTextContainer className={clsx(enabled && classes.TokenTextContainer)} {...props}>
{children}
</StyledTokenTextContainer>
)
}

export default TokenTextContainer
Loading
Loading