-
Notifications
You must be signed in to change notification settings - Fork 2
Implement the GratiText Figma designs across marketing, auth, recipients, and settings #98
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
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
b2ab3eb
Implement Figma design foundations, app chrome, and landing page
cursoragent 6af391d
Restyle auth screens to the Figma designs
cursoragent b533350
Redesign recipients list and recipient editor
cursoragent 7464754
Redesign the recipient thread page with sidebar, bubbles, and composer
cursoragent b5ceb13
Redesign account settings with inline editing and dialog sub-pages
cursoragent 7023f5d
Polish mobile and dark mode details after QA against the Figma frames
cursoragent d6c5d4b
Re-extract landing illustrations without stray fragments
cursoragent 5374d74
Fix the green band's wave dividers
cursoragent 8155e7a
Bring the landing page in line with the Figma frames
cursoragent 5ae5954
Re-cut the CTA flower with its full outline
cursoragent 557361d
Prefill signup from the landing CTA and send members to recipients
cursoragent 8586696
Accept phone numbers in the login identifier field
cursoragent d7c8cfd
Only mark DialogPage as a modal when it floats
cursoragent File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| import { type ReactNode, useCallback, useRef } from 'react' | ||
| import { Link, useNavigate } from 'react-router' | ||
| import { useFocusTrap } from '#app/utils/focus-trap.ts' | ||
| import { useMediaQuery } from '#app/utils/media-query.ts' | ||
| import { cn } from '#app/utils/misc.tsx' | ||
| import { Icon } from './ui/icon.tsx' | ||
|
|
||
| /** | ||
| * A route rendered as a modal dialog over its parent page on desktop and as a | ||
| * plain full-width page (with a "go back" link) on phones — the pattern the | ||
| * account settings sub-pages use in the designs. | ||
| */ | ||
| export function DialogPage({ | ||
| title, | ||
| description, | ||
| backTo, | ||
| backLabel = 'Go back without changes', | ||
| hideClose = false, | ||
| children, | ||
| className, | ||
| }: { | ||
| title: string | ||
| description?: ReactNode | ||
| backTo: string | ||
| backLabel?: string | ||
| /** Skip the corner close button when the content has its own Cancel. */ | ||
| hideClose?: boolean | ||
| children: ReactNode | ||
| className?: string | ||
| }) { | ||
| const navigate = useNavigate() | ||
| const panelRef = useRef<HTMLDivElement>(null) | ||
| const close = useCallback(() => { | ||
| void navigate(backTo) | ||
| }, [navigate, backTo]) | ||
| // The panel is only a modal when it floats over the page (md and up); on | ||
| // phones it is an ordinary page and the header stays usable. | ||
| const isDesktop = useMediaQuery('(min-width: 768px)') | ||
| useFocusTrap(panelRef, isDesktop, close) | ||
|
|
||
| return ( | ||
| <div className="flex flex-1 flex-col md:fixed md:inset-0 md:z-50 md:flex-none md:items-center md:justify-center md:p-6"> | ||
| <Link | ||
| to={backTo} | ||
| tabIndex={-1} | ||
| aria-hidden="true" | ||
| className="bg-overlay/70 absolute inset-0 hidden backdrop-blur-[4px] md:block" | ||
| /> | ||
| <div | ||
| ref={panelRef} | ||
| role={isDesktop ? 'dialog' : undefined} | ||
| aria-modal={isDesktop ? 'true' : undefined} | ||
| aria-labelledby="dialog-page-title" | ||
| tabIndex={-1} | ||
| className={cn( | ||
| 'md:bg-card md:text-card-foreground container flex flex-1 flex-col pt-6 pb-6 outline-none md:relative md:max-h-full md:w-full md:max-w-[46.5rem] md:flex-none md:overflow-y-auto md:rounded-[1.75rem] md:px-12 md:py-11 md:shadow-[0_32px_64px_-24px_rgba(24,36,48,0.35)]', | ||
| className, | ||
| )} | ||
| > | ||
| <Link | ||
| to={backTo} | ||
| className="text-foreground hover:text-brand -ml-1 inline-flex w-fit items-center gap-2 text-base transition-colors md:hidden" | ||
| > | ||
| <Icon name="chevron-left" size="sm" aria-hidden="true" /> | ||
| {backLabel} | ||
| </Link> | ||
| {hideClose ? null : ( | ||
| <Link | ||
| to={backTo} | ||
| aria-label="Close" | ||
| className="text-muted-foreground hover:bg-muted hover:text-foreground absolute top-6 right-6 hidden h-10 w-10 items-center justify-center rounded-full transition-colors md:flex" | ||
| > | ||
| <Icon name="cross-1" size="sm" aria-hidden="true" /> | ||
| </Link> | ||
| )} | ||
| <h1 | ||
| id="dialog-page-title" | ||
| className="font-display text-foreground md:text-h3 sr-only md:not-sr-only" | ||
| > | ||
| {title} | ||
| </h1> | ||
| {description ? ( | ||
| <p className="text-muted-foreground hidden text-base md:mt-4 md:block"> | ||
| {description} | ||
| </p> | ||
| ) : null} | ||
| <div className="mt-6 flex flex-1 flex-col md:mt-8 md:flex-none"> | ||
| {children} | ||
| </div> | ||
| </div> | ||
| </div> | ||
| ) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| import { type ReactNode } from 'react' | ||
| import { cn } from '#app/utils/misc.tsx' | ||
|
|
||
| /** | ||
| * Auth routes export this handle so the app layout tints the header to match | ||
| * the full-bleed beige auth page on small screens and drops the sign-up / | ||
| * log-in buttons that would compete with the form. | ||
| */ | ||
| export const authPageHandle = { pageTint: 'hero', chrome: 'minimal' } as const | ||
|
|
||
| /** Dashboard forms keep the normal header but share the auth page shell. */ | ||
| export const formPageHandle = { pageTint: 'hero' } as const | ||
|
|
||
| export function FormPage({ | ||
| title, | ||
| description, | ||
| children, | ||
| footer, | ||
| as = 'main', | ||
| }: { | ||
| title: string | ||
| description?: ReactNode | ||
| children: ReactNode | ||
| footer?: ReactNode | ||
| /** Use `div` when an ancestor route already renders the `main` landmark. */ | ||
| as?: 'main' | 'div' | ||
| }) { | ||
| const Wrapper = as | ||
| return ( | ||
| <Wrapper className="bg-hero md:bg-background flex flex-1 flex-col pt-10 pb-6 md:pt-20 md:pb-24"> | ||
| <div className="container flex flex-1 flex-col md:max-w-[52rem] md:flex-none"> | ||
| <div className="text-center"> | ||
| <h1 className="font-display text-foreground md:text-h2 text-[1.875rem] leading-[1.15] text-balance"> | ||
| {title} | ||
| </h1> | ||
| {description ? ( | ||
| <p className="text-muted-foreground mx-auto mt-4 max-w-xl text-base text-pretty md:mt-5"> | ||
| {description} | ||
| </p> | ||
| ) : null} | ||
| </div> | ||
| <div className="md:bg-surface md:text-surface-foreground mt-10 flex flex-1 flex-col md:mt-14 md:flex-none md:rounded-[1.5rem] md:px-9 md:py-9 lg:px-9"> | ||
| {children} | ||
| </div> | ||
| {footer ? ( | ||
| <div className="text-muted-foreground [&_a]:text-foreground mt-6 text-center text-sm md:mt-8 [&_a]:inline-flex [&_a]:items-center [&_a]:gap-2 [&_a]:font-medium [&_a]:underline-offset-4 hover:[&_a]:underline"> | ||
| {footer} | ||
| </div> | ||
| ) : null} | ||
| </div> | ||
| </Wrapper> | ||
| ) | ||
| } | ||
|
|
||
| /** | ||
| * Bottom row of an auth form: optional helper text on the left and the | ||
| * primary action on the right. On phones the action becomes a full-width | ||
| * button anchored to the bottom of the screen, as in the mobile designs. | ||
| */ | ||
| export function FormActions({ | ||
| aside, | ||
| children, | ||
| className, | ||
| }: { | ||
| aside?: ReactNode | ||
| children: ReactNode | ||
| className?: string | ||
| }) { | ||
| return ( | ||
| <div | ||
| className={cn( | ||
| 'mt-auto flex flex-col gap-5 pt-6 md:mt-0 md:flex-row md:items-center md:justify-between md:gap-6 md:pt-2', | ||
| className, | ||
| )} | ||
| > | ||
| <div className="text-foreground [&_a]:text-foreground text-sm [&_a]:underline-offset-4 hover:[&_a]:underline"> | ||
| {aside} | ||
| </div> | ||
| <div className="flex flex-col gap-3 md:flex-row md:items-center [&>*]:w-full md:[&>*]:w-auto"> | ||
| {children} | ||
| </div> | ||
| </div> | ||
| ) | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.