Custom Component skill
The bestax-custom-component
skill teaches an agent to build a new custom component the bestax way — starting by
checking whether a matching component already exists, then the helper hooks and the Bulma v1
SCSS pattern, plus stories, tests, and docs.
Install
npx skills add https://github.com/allxsmith/bestax --skill bestax-custom-component
Example
An agent was asked to build a ProfileCard and followed the skill.
Prompt
Build a ProfileCard component — avatar on top, then name, role, and a short
description — following the bestax custom-component skill.
There is no ProfileCard in the library — but Card, Image, Title, SubTitle, and
Content already exist. So rather than reinventing them, ProfileCard is a new component that
composes the existing elements and adds only its own layout. (Had the request been a "label"
or "badge", the skill would have stopped and pointed at the existing Tag / Tags.)
Live result
function example() { return ( <div style={{ display: 'flex', gap: '1.25rem', flexWrap: 'wrap' }}> <ProfileCard name="Ada Lovelace" role="Mathematician & Writer" imageSrc="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a4/Ada_Lovelace_portrait.jpg/330px-Ada_Lovelace_portrait.jpg" > English mathematician who worked on Charles Babbage's proposed Analytical Engine. She was the first to recognise that the machine had applications beyond pure calculation, and published what is now regarded as the first algorithm intended to be carried out by a machine. </ProfileCard> <ProfileCard name="Alan Turing" role="Computer Scientist" imageSrc="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a1/Alan_Turing_Aged_16.jpg/330px-Alan_Turing_Aged_16.jpg" > British mathematician and logician who formalised the concepts of algorithm and computation with the Turing machine. His wartime codebreaking at Bletchley Park helped shorten the Second World War, and he is widely considered the father of theoretical computer science. </ProfileCard> </div> ); }
Generated code
- ProfileCard.tsx
- _profilecard.scss
- Usage
import React, { forwardRef } from 'react';
import { classNames, usePrefixedClassNames } from '../helpers/classNames';
import { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';
// Compose the existing elements rather than reinventing them.
import { Image, Title, SubTitle, Content } from '../index';
export interface ProfileCardProps
extends
Omit<React.HTMLAttributes<HTMLDivElement>, 'title' | 'color'>,
BulmaClassesProps {
name: string;
role?: string;
imageSrc: string;
imageAlt?: string;
}
export const ProfileCard = forwardRef<HTMLDivElement, ProfileCardProps>(
({ name, role, imageSrc, imageAlt, className, children, ...props }, ref) => {
const { bulmaHelperClasses, rest } = useBulmaClasses(props);
const cardClass = usePrefixedClassNames('profile-card');
const imageClass = usePrefixedClassNames('profile-card-image');
const bodyClass = usePrefixedClassNames('profile-card-body');
const combined = classNames(cardClass, bulmaHelperClasses, className);
return (
<div ref={ref} className={combined} {...rest}>
<div className={imageClass}>
<Image
size="128x128"
src={imageSrc}
alt={imageAlt ?? name}
isRounded
/>
</div>
<div className={bodyClass}>
<Title size="4" as="h3">
{name}
</Title>
{role && (
<SubTitle size="6" as="p">
{role}
</SubTitle>
)}
{children && <Content>{children}</Content>}
</div>
</div>
);
}
);
ProfileCard.displayName = 'ProfileCard';
export default ProfileCard;
@use 'bulma/sass/utilities/initial-variables' as iv;
@use 'bulma/sass/utilities/css-variables' as cv;
$profile-card-width: 20rem !default;
$profile-card-min-height: 26rem !default;
$profile-card-avatar-size: 9rem !default;
$profile-card-background: cv.getVar('scheme-main') !default;
$profile-card-border: cv.getVar('border-weak') !default;
$profile-card-radius: cv.getVar('radius-large') !default;
$profile-card-shadow: 0 0.5em 1em -0.125em hsla(0, 0%, 4%, 0.1) !default;
$profile-card-padding: 1.5rem !default;
$profile-card-gap: 0.75rem !default;
.#{iv.$class-prefix}profile-card {
@include cv.register-vars(
(
'profile-card-width': #{$profile-card-width},
'profile-card-min-height': #{$profile-card-min-height},
'profile-card-avatar-size': #{$profile-card-avatar-size},
'profile-card-background': #{$profile-card-background},
'profile-card-border': #{$profile-card-border},
'profile-card-radius': #{$profile-card-radius},
'profile-card-shadow': #{$profile-card-shadow},
'profile-card-padding': #{$profile-card-padding},
'profile-card-gap': #{$profile-card-gap},
)
);
}
.#{iv.$class-prefix}profile-card {
display: flex;
flex-direction: column;
align-items: center; // horizontal centering of the column
text-align: center;
width: cv.getVar('profile-card-width');
min-height: cv.getVar('profile-card-min-height');
max-width: 100%;
gap: cv.getVar('profile-card-gap');
padding: cv.getVar('profile-card-padding');
background-color: cv.getVar('profile-card-background');
border: 1px solid cv.getVar('profile-card-border');
border-radius: cv.getVar('profile-card-radius');
box-shadow: cv.getVar('profile-card-shadow');
}
.#{iv.$class-prefix}profile-card-image {
display: flex;
justify-content: center;
}
// Enlarge the composed avatar and crop (not stretch) the portrait to a circle.
.#{iv.$class-prefix}profile-card-image .#{iv.$class-prefix}image {
width: cv.getVar('profile-card-avatar-size');
height: cv.getVar('profile-card-avatar-size');
}
.#{iv.$class-prefix}profile-card-image .#{iv.$class-prefix}image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.#{iv.$class-prefix}profile-card-body {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
import { ProfileCard } from '@allxsmith/bestax-bulma';
<ProfileCard name="Ada Lovelace" role="Mathematician" imageSrc="/ada.jpg">
Wrote the first algorithm intended to be carried out by a machine.
</ProfileCard>;
ProfileCard is an example produced by the skill — it is not part of the published
@allxsmith/bestax-bulma package. The point is the pattern (check first, compose existing
elements, follow the SCSS convention, then inspect it in a browser to confirm spacing and
alignment). See the full skill on
GitHub.