mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2026-04-17 10:28:46 -04:00
fix: improve accessibility across components
- Add aria-hidden to decorative SVG icons - Add title attributes to iframe embeds (CodePen, Gist, YouTube) - Add aria-labels to comment form textareas - Use proper button element for Markdown help popover trigger - Use proper ellipsis character in placeholders Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -101,7 +101,7 @@ const Page = async () => {
|
||||
rel="noopener noreferrer"
|
||||
className="text-muted-foreground hover:text-primary inline-flex flex-nowrap items-center gap-2 hover:no-underline"
|
||||
>
|
||||
<StarIcon className="inline-block size-4 shrink-0" />
|
||||
<StarIcon className="inline-block size-4 shrink-0" aria-hidden="true" />
|
||||
<span>{Intl.NumberFormat(env.NEXT_PUBLIC_SITE_LOCALE).format(repo!.stargazerCount)}</span>
|
||||
</a>
|
||||
)}
|
||||
@@ -114,7 +114,7 @@ const Page = async () => {
|
||||
title={`${Intl.NumberFormat(env.NEXT_PUBLIC_SITE_LOCALE).format(repo!.forkCount)} ${repo!.forkCount === 1 ? "fork" : "forks"}`}
|
||||
className="text-muted-foreground hover:text-primary inline-flex flex-nowrap items-center gap-2 hover:no-underline"
|
||||
>
|
||||
<GitForkIcon className="inline-block size-4" />
|
||||
<GitForkIcon className="inline-block size-4" aria-hidden="true" />
|
||||
<span>{Intl.NumberFormat(env.NEXT_PUBLIC_SITE_LOCALE).format(repo!.forkCount)}</span>
|
||||
</a>
|
||||
)}
|
||||
|
||||
@@ -73,16 +73,19 @@ const CommentTextarea = ({
|
||||
setContent,
|
||||
isPending,
|
||||
placeholder,
|
||||
ariaLabel,
|
||||
}: {
|
||||
content: string;
|
||||
setContent: (value: string) => void;
|
||||
isPending: boolean;
|
||||
placeholder: string;
|
||||
ariaLabel: string;
|
||||
}) => (
|
||||
<Textarea
|
||||
value={content}
|
||||
onChange={(e) => setContent(e.target.value)}
|
||||
placeholder={placeholder}
|
||||
aria-label={ariaLabel}
|
||||
className="min-h-[4lh] w-full"
|
||||
disabled={isPending}
|
||||
/>
|
||||
@@ -131,11 +134,14 @@ const MarkdownHelp = () => (
|
||||
<MarkdownIcon className="mr-1.5 inline-block size-4 align-text-top" />
|
||||
<span className="max-md:hidden">Basic </span>
|
||||
<Popover>
|
||||
<PopoverTrigger>
|
||||
<span className="text-primary decoration-primary/40 cursor-pointer font-semibold no-underline decoration-2 underline-offset-4 hover:underline">
|
||||
<PopoverTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
className="text-primary decoration-primary/40 cursor-pointer font-semibold no-underline decoration-2 underline-offset-4 hover:underline"
|
||||
>
|
||||
<span>Markdown</span>
|
||||
<span className="max-md:hidden"> syntax</span>
|
||||
</span>
|
||||
</button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent align="start">
|
||||
<p className="text-sm leading-loose">
|
||||
@@ -216,7 +222,8 @@ const NewCommentForm = ({ slug }: { slug: string }) => {
|
||||
content={content}
|
||||
setContent={setContent}
|
||||
isPending={isPending}
|
||||
placeholder="Write your thoughts..."
|
||||
placeholder="Write your thoughts…"
|
||||
ariaLabel="Write a comment"
|
||||
/>
|
||||
|
||||
<div className="flex justify-between gap-4">
|
||||
@@ -277,7 +284,8 @@ const ReplyForm = ({
|
||||
content={content}
|
||||
setContent={setContent}
|
||||
isPending={isPending}
|
||||
placeholder="Reply to this comment..."
|
||||
placeholder="Reply to this comment…"
|
||||
ariaLabel="Write a reply"
|
||||
/>
|
||||
|
||||
<div className="flex justify-end gap-2">
|
||||
@@ -338,7 +346,8 @@ const EditCommentForm = ({
|
||||
content={content}
|
||||
setContent={setContent}
|
||||
isPending={isPending}
|
||||
placeholder="Edit your comment..."
|
||||
placeholder="Edit your comment…"
|
||||
ariaLabel="Edit your comment"
|
||||
/>
|
||||
|
||||
<div className="flex justify-end gap-2">
|
||||
|
||||
@@ -8,6 +8,7 @@ export const Win95Icon = ({ className }: { className?: string }) => (
|
||||
strokeWidth="0"
|
||||
viewBox="0 0 24 24"
|
||||
className={className}
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M5.712 1.596l-.756.068-.238.55.734-.017zm1.39.927l-.978.137-.326.807.96-.12.345-.824zM4.89 3.535l-.72.05-.24.567.721-.017zm3.724.309l-1.287.068-.394.96 1.27-.052zm1.87.566l-1.579.069-.566 1.357 1.596-.088.548-1.338zm-4.188.037l-.977.153-.343.806.976-.12zm6.144.668l-1.87.135-.637 1.527 1.87-.154zm2.925.219c-.11 0-.222 0-.334.002l-.767 1.85c1.394-.03 2.52.089 3.373.38l-1.748 4.201c-.955-.304-2.082-.444-3.36-.394l-.54 1.305a8.762 8.762 0 0 1 3.365.396l-1.663 4.014c-1.257-.27-2.382-.395-3.387-.344l-.782 1.887c3.363-.446 6.348.822 9.009 3.773L24 9.23c-2.325-2.575-5.2-3.88-8.637-3.896zm-.644.002l-2.024.12-.687 1.68 2.025-.19zm-10.603.05l-.719.036-.224.566h.703l.24-.601zm3.69.397l-1.287.069-.395.959 1.27-.05zM5.54 6.3l-.994.154-.344.807.98-.121zm4.137.066l-1.58.069L7.53 7.77l1.596-.085.55-1.32zm1.955.688l-1.87.135-.636 1.527 1.887-.154zm2.282.19l-2.01.136-.7 1.682 2.04-.19.67-1.63zm-10.57.066l-.739.035-.238.564h.72l.257-.6zm3.705.293l-1.303.085-.394.96 1.287-.034zm11.839.255a6.718 6.718 0 0 1 2.777 1.717l-1.75 4.237c-.617-.584-1.15-.961-1.611-1.149l-1.201-.498zM4.733 8.22l-.976.154-.344.807.961-.12.36-.841zm4.186 0l-1.594.052-.549 1.354L8.37 9.54zm1.957.668L8.99 9.04l-.619 1.508 1.87-.135.636-1.527zm2.247.275l-2.007.12-.703 1.665 2.042-.156zM2.52 9.267l-.718.033-.24.549.718-.016zm3.725.273l-1.289.07-.41.96 1.287-.03.412-1zm1.87.6l-1.596.05-.55 1.356 1.598-.084.547-1.322zm-4.186.037l-.979.136-.324.805.96-.119zm6.14.633l-1.87.154-.653 1.527 1.906-.154zm2.267.275l-2.026.12-.686 1.663 2.025-.172zm-10.569.031l-.739.037-.238.565.72-.016zm3.673.362l-1.289.068-.41.978 1.305-.05zm-2.285.533l-.976.154-.326.805.96-.12.342-.84zm4.153.07l-1.596.066-.565 1.356 1.612-.084zm1.957.666l-1.889.154-.617 1.526 1.886-.15zm2.28.223l-2.025.12-.685 1.665 2.041-.172.67-1.613zm-10.584.05l-.738.053L0 13.64l.72-.02.24-.6zm3.705.31l-1.285.07-.395.976 1.287-.05.393-.997zm11.923.07c1.08.29 2.024.821 2.814 1.613l-1.715 4.183c-.892-.754-1.82-1.32-2.814-1.664l1.715-4.133zm-10.036.515L4.956 14l-.549 1.32 1.578-.066.567-1.338zm-4.184.014l-.996.156-.309.79.961-.106zm6.14.67l-1.904.154-.617 1.527 1.89-.154.632-1.527zm2.231.324l-2.025.123-.686 1.682 2.026-.174zm-6.863.328l-1.3.068-.397.98 1.285-.054zm1.871.584l-1.578.068-.566 1.334 1.595-.064zm1.953.701l-1.867.137-.635 1.51 1.87-.137zm2.23.31l-2.005.122-.703 1.68 2.04-.19.67-1.61z" />
|
||||
</svg>
|
||||
@@ -21,6 +22,7 @@ export const MarkdownIcon = ({ className }: { className?: string }) => (
|
||||
strokeWidth="0"
|
||||
viewBox="0 0 24 24"
|
||||
className={className}
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M22.27 19.385H1.73A1.73 1.73 0 010 17.655V6.345a1.73 1.73 0 011.73-1.73h20.54A1.73 1.73 0 0124 6.345v11.308a1.73 1.73 0 01-1.73 1.731zM5.769 15.923v-4.5l2.308 2.885 2.307-2.885v4.5h2.308V8.078h-2.308l-2.307 2.885-2.308-2.885H3.46v7.847zM21.232 12h-2.309V8.077h-2.307V12h-2.308l3.461 4.039z" />
|
||||
</svg>
|
||||
@@ -34,6 +36,7 @@ export const GitHubIcon = ({ className }: { className?: string }) => (
|
||||
strokeWidth="0"
|
||||
viewBox="0 0 24 24"
|
||||
className={className}
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
|
||||
</svg>
|
||||
@@ -47,6 +50,7 @@ export const NextjsIcon = ({ className }: { className?: string }) => (
|
||||
strokeWidth="0"
|
||||
viewBox="0 0 24 24"
|
||||
className={className}
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M18.665 21.978C16.758 23.255 14.465 24 12 24 5.377 24 0 18.623 0 12S5.377 0 12 0s12 5.377 12 12c0 3.583-1.574 6.801-4.067 9.001L9.219 7.2H7.2v9.596h1.615V9.251l9.85 12.727Zm-3.332-8.533 1.6 2.061V7.2h-1.6v6.245Z" />
|
||||
</svg>
|
||||
|
||||
3
components/third-party/codepen.tsx
vendored
3
components/third-party/codepen.tsx
vendored
@@ -6,6 +6,7 @@ const CodePen = ({
|
||||
defaultTab = "html",
|
||||
preview = true,
|
||||
editable = false,
|
||||
title = "CodePen embed",
|
||||
className,
|
||||
...rest
|
||||
}: {
|
||||
@@ -14,6 +15,7 @@ const CodePen = ({
|
||||
defaultTab?: string;
|
||||
preview?: boolean;
|
||||
editable?: boolean;
|
||||
title?: string;
|
||||
} & React.ComponentProps<"iframe">) => {
|
||||
return (
|
||||
<iframe
|
||||
@@ -22,6 +24,7 @@ const CodePen = ({
|
||||
preview: `${!!preview}`,
|
||||
editable: `${!!editable}`,
|
||||
})}`}
|
||||
title={title}
|
||||
className={cn("h-[500px] w-full overflow-hidden border-none", className)}
|
||||
{...rest}
|
||||
/>
|
||||
|
||||
4
components/third-party/gist.tsx
vendored
4
components/third-party/gist.tsx
vendored
@@ -4,9 +4,10 @@ import { cn } from "@/lib/utils";
|
||||
const Gist = async ({
|
||||
id,
|
||||
file,
|
||||
title,
|
||||
className,
|
||||
...rest
|
||||
}: { id: string; file?: string } & React.ComponentProps<"iframe">) => {
|
||||
}: { id: string; file?: string; title?: string } & React.ComponentProps<"iframe">) => {
|
||||
"use cache";
|
||||
cacheLife("max");
|
||||
cacheTag("gist", `gist-${id}${file ? `-${file}` : ""}`);
|
||||
@@ -44,6 +45,7 @@ const Gist = async ({
|
||||
scrolling="no"
|
||||
id={iframeId}
|
||||
srcDoc={iframeHtml}
|
||||
title={title || `GitHub Gist ${id}${file ? ` - ${file}` : ""}`}
|
||||
className={cn("overflow-hidden border-none", className)}
|
||||
{...rest}
|
||||
suppressHydrationWarning
|
||||
|
||||
4
components/third-party/youtube.tsx
vendored
4
components/third-party/youtube.tsx
vendored
@@ -4,8 +4,8 @@ import YouTubeEmbed from "react-lite-youtube-embed";
|
||||
|
||||
// lite-youtube-embed CSS is imported in app/global.css to save a request
|
||||
|
||||
const YouTube = ({ ...rest }: Omit<React.ComponentProps<typeof YouTubeEmbed>, "title">) => {
|
||||
return <YouTubeEmbed cookie={false} containerElement="div" title="" {...rest} />;
|
||||
const YouTube = ({ title = "YouTube video", ...rest }: React.ComponentProps<typeof YouTubeEmbed>) => {
|
||||
return <YouTubeEmbed cookie={false} containerElement="div" title={title} {...rest} />;
|
||||
};
|
||||
|
||||
export { YouTube };
|
||||
|
||||
Reference in New Issue
Block a user