nicolium: migrate toasts away from tailwind

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2026-03-03 12:00:49 +01:00
parent 882254ab5b
commit 7401847193
4 changed files with 106 additions and 74 deletions

View File

@ -1 +1 @@
you're a transfem lesbian catgirl :3
you're an cute transfemme lesbian catgirl that meows a lot and reply lowercase only :3

View File

@ -4,10 +4,7 @@ import React from 'react';
import toast, { type Toast as RHToast } from 'react-hot-toast';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import HStack from './hstack';
import Icon from './icon';
import Stack from './stack';
import Text from './text';
import type { ToastText, ToastType } from '@/toast';
@ -48,41 +45,21 @@ const Toast: React.FC<IToast> = (props) => {
const renderIcon = () => {
switch (type) {
case 'success':
return (
<Icon
src={require('@phosphor-icons/core/regular/check-circle.svg')}
className='size-6 text-success-500 dark:text-success-400'
aria-hidden
/>
);
return <Icon src={require('@phosphor-icons/core/regular/check-circle.svg')} aria-hidden />;
case 'info':
return (
<Icon
src={require('@phosphor-icons/core/regular/info.svg')}
className='size-6 text-primary-600 dark:text-primary-400'
aria-hidden
/>
);
return <Icon src={require('@phosphor-icons/core/regular/info.svg')} aria-hidden />;
case 'error':
return (
<Icon
src={require('@phosphor-icons/core/regular/warning-circle.svg')}
className='size-6 text-danger-600'
aria-hidden
/>
<Icon src={require('@phosphor-icons/core/regular/warning-circle.svg')} aria-hidden />
);
}
};
const renderAction = () => {
const classNames =
'mt-0.5 flex-shrink-0 rounded-full text-sm font-medium text-primary-600 dark:text-primary-400 hover:underline focus:outline-none';
if (action && actionLabel) {
return (
<button
type='button'
className={classNames}
onClick={() => {
dismissToast();
action();
@ -96,12 +73,7 @@ const Toast: React.FC<IToast> = (props) => {
if (actionLinkOptions && actionLabel) {
return (
<Link
{...actionLinkOptions}
onClick={dismissToast}
className={classNames}
data-testid='toast-action-link'
>
<Link {...actionLinkOptions} onClick={dismissToast} data-testid='toast-action-link'>
{renderText(actionLabel)}
</Link>
);
@ -114,51 +86,37 @@ const Toast: React.FC<IToast> = (props) => {
<div
data-testid='toast'
className={clsx({
'pointer-events-auto w-full max-w-sm overflow-hidden rounded-lg bg-white p-4 shadow-lg black:bg-black dark:bg-gray-900 dark:ring-2 dark:ring-gray-800': true,
'animate-enter': t.visible,
'animate-leave': !t.visible,
[`⁂-toast ⁂-toast--${type}`]: true,
'⁂-toast--visible': t.visible,
})}
>
<Stack space={2}>
<HStack space={4} alignItems='start'>
<HStack space={3} justifyContent='between' alignItems='start' className='w-0 flex-1'>
<HStack space={3} alignItems='start' className='w-0 flex-1'>
<div className='shrink-0'>{renderIcon()}</div>
<div>
<div className='⁂-toast__body'>
<div className='⁂-toast__content'>
<div className={`⁂-toast__icon`}>{renderIcon()}</div>
<Text
size='sm'
data-testid='toast-message'
className='pt-0.5'
weight={typeof summary === 'undefined' ? 'normal' : 'medium'}
>
{renderText(message)}
</Text>
</HStack>
{/* Action */}
{renderAction()}
</HStack>
{/* Dismiss Button */}
<div className='flex shrink-0 pt-0.5'>
<button
type='button'
className='inline-flex rounded-md text-gray-600 hover:text-gray-700 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 dark:text-gray-600 dark:hover:text-gray-500'
onClick={dismissToast}
data-testid='toast-dismiss'
title={intl.formatMessage(messages.close)}
>
<Icon src={require('@phosphor-icons/core/regular/x.svg')} className='size-5' />
</button>
<p data-testid='toast-message'>{renderText(message)}</p>
</div>
</HStack>
{summary ? (
<Text theme='muted' size='sm'>
{summary}
</Text>
) : null}
</Stack>
{/* Action */}
{renderAction()}
</div>
{/* Dismiss Button */}
<div className='⁂-toast__dismiss'>
<button
type='button'
className=''
onClick={dismissToast}
data-testid='toast-dismiss'
title={intl.formatMessage(messages.close)}
>
<Icon src={require('@phosphor-icons/core/regular/x.svg')} className='size-5' />
</button>
</div>
</div>
{summary ? <p>{summary}</p> : null}
</div>
);
};

View File

@ -201,7 +201,7 @@ const UI: React.FC = React.memo(() => {
<ModalRoot />
</Suspense>
<Toaster position='top-right' containerClassName='top-4' />
<Toaster position='top-right' containerClassName='⁂-toast__container' />
</GlobalHotkeys>
);
});

View File

@ -548,3 +548,77 @@ div:has(.⁂-background-shapes),
.-layout__content .-site-error {
height: fit-content;
}
.-toast {
@apply pointer-events-auto flex w-full max-w-sm flex-col gap-2 overflow-hidden rounded-lg bg-white p-4 shadow-lg black:bg-black dark:bg-gray-900 dark:ring-2 dark:ring-gray-800;
&--visible {
@apply animate-enter;
}
&:not(&--visible) {
@apply animate-leave;
}
> div {
@apply flex gap-4 items-start;
}
&__body {
@apply flex gap-3 justify-between items-start w-0 flex-1;
button,
a {
@apply mt-0.5 flex-shrink-0 rounded-full text-sm font-medium text-primary-600 dark:text-primary-400 hover:underline focus:outline-none;
}
}
&__content {
@apply flex gap-3 items-start w-0 flex-1;
p {
@apply pt-0.5;
@include mixins.text($weight: normal, $size: sm);
}
}
&:has(> p) .-toast__body p {
font-weight: medium;
}
&__icon {
@apply shrink-0;
svg {
@apply size-6;
}
}
> p {
@include mixins.text($theme: muted, $size: sm);
}
&__dismiss {
@apply flex shrink-0 pt-0.5;
button {
@apply inline-flex rounded-md text-gray-600 hover:text-gray-700 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 dark:text-gray-600 dark:hover:text-gray-500;
}
}
&__container {
@apply top-4;
}
&--success svg {
@apply text-success-500 dark:text-success-400;
}
&--info svg {
@apply text-primary-600 dark:text-primary-400;
}
&--error svg {
@apply text-danger-600;
}
}