<div id="root"></div>
body {
display: grid;
place-content: center;
height: 100vh;
}
#root {
> * + * {
margin-top: 0.5rem;
}
}
View Compiled
import React, {
useState,
ReactNode,
useCallback,
KeyboardEvent,
MouseEvent,
HTMLAttributes,
HTMLButtonElement
} from "https://esm.sh/react@18";
import { createRoot } from "https://esm.sh/react-dom@18";
type ButtonProps = {
onClick?: (e: MouseEvent) => void;
children?: ReactNode;
className?: string;
disabled?: boolean;
} & HTMLAttributes<HTMLButtonElement>;
const Button = ({
onClick,
children,
className = "",
disabled = false,
...props
}: ButtonProps) => {
const handleKeyDown = useCallback(
(e: KeyboardEvent) => {
if (disabled) return;
if (e.key === " " || e.key === "Enter") {
e.preventDefault();
onClick?.((e as unknown) as MouseEvent);
}
},
[onClick, disabled]
);
const handleClick = (e: MouseEvent) => {
if (disabled) return;
onClick?.(e);
};
return (
<div
{...props}
tabIndex={disabled ? -1 : 0}
onClick={handleClick}
onKeyDown={handleKeyDown}
aria-disabled={disabled}
className={`
inline-block px-4 py-2
text-sm
bg-blue-500 text-white
rounded cursor-pointer select-none
transition-all duration-200
/* ホバー時のスタイル */
hover:bg-blue-600
/* フォーカス時のスタイル */
focus:outline-none
focus:ring-2
focus:ring-blue-400
focus:ring-offset-2
/* アクティブ時のスタイル */
active:bg-blue-700
/* 非活性時のスタイル */
${
disabled &&
`
bg-gray-200
text-gray-400
cursor-not-allowed
hover:bg-gray-200
hover:shadow-none
focus:ring-0
focus:ring-offset-0
pointer-events-none
opacity-80
`
}
`}
>
{children}
</div>
);
};
const App = () => {
return (
<>
<div>
<Button onClick={() => alert("Clicked!")}>Pure Div Button</Button>
</div>
<div>
<Button disabled onClick={() => alert("Clicked!")}>
Pure Div Button (disabled)
</Button>
</div>
</>
);
};
const rootElement = document.getElementById("root");
if (rootElement) {
const root = createRoot(rootElement);
root.render(<App />);
}
View Compiled
This Pen doesn't use any external CSS resources.