Once you've created a project and installed dependencies with yarn install
yarn run dev
# or start the server and open the app in a new browser tab
yarn run dev -- --open
To create a production version of your app:
yarn run build
getContext() setContext()
$props()
{@render children()}
{#if component.visibility === ’’hidden”}
{:else if component.visibility === ’’hidden”}
// show me
{:else}
// not much here
{/if}
{@render children?.()}
{@render children?.()}
You can preview the production build with npm run preview
.
To deploy your app, you may need to install an adapter for your target environment.
In the evolving landscape of frontend development, we've discovered that the most powerful solutions often come from understanding and working with fundamental patterns rather than fighting against them. PatternFly Svelte represents a breakthrough in component development - not because it adds new complexities, but because it removes unnecessary constraints.
Our approach emerged from a deep understanding of web platform fundamentals and years of experience with various component systems. We learned that the best solutions come not from adding more abstractions, but from embracing the platform's natural strengths.
Consider a traditional component implementation:
interface ButtonProps extends BaseProps, InteractiveProps {
variant: 'primary' | 'secondary';
size: 'small' | 'medium' | 'large';
isDisabled?: boolean;
onClick?: (event: Event) => void;
// Many more specific props
}
Now compare it to our approach:
let {
children,
...props
} = $props();
This isn't just less code - it's more powerful. By removing unnecessary constraints, we create components that can adapt to needs we haven't even discovered yet.
Every component in our system follows a simple, consistent pattern:
<script lang="ts">
import { getClasses } from "$functions/utils";
import { GENERIC_MODIFIERS } from "$lib/constants";
let {
tag = 'div',
class: className = 'pf-v6-c-card',
children,
...props
} = $props();
const states = $derived({
...GENERIC_MODIFIERS,
...props
});
const classes = getClasses(className, states);
</script>
<svelte:element this={tag} class={classes} {...props}>
{@render children?.()}
</svelte:element>
We leverage Vite's plugin system to make common utilities available throughout our application:
// vite.config.js
plugins: [
{
name: 'global-utilities',
transform(code, id) {
if (id.endsWith('.svelte')) {
return {
code: `
import { camelCase, pascalCase, kebabCase } from 'case-anything';
import { getClasses } from '$functions/utils';
import { GENERIC_MODIFIERS } from '$lib/constants';
${code}
`,
map: null
};
}
}
}
]
Instead of creating specific states for each component, we provide a set of generic modifiers that can be used consistently across our system:
export const GENERIC_MODIFIERS = {
isExpanded: false,
isDisabled: false,
isVisible: false,
isHidden: false,
isSelected: false,
isActive: false,
isPrimary: false,
// Additional states can be added as needed
};
Our class generation utility transforms states into BEM-style modifiers:
export function getClasses(templateClass: string, templateStates: Record<string, boolean>) {
const stateClasses = Object.entries(templateStates)
.filter(([_, value]) => value)
.map(([key]) => `pf-m-${key.replace('is', '').toLowerCase()}`)
.join(' ');
return `${templateClass} ${stateClasses}`.trim();
}
Components can be used with minimal configuration:
<Card>Simple content</Card>
States can be applied naturally:
<Card
isExpanded={true}
isPrimary={true}
>
Content adapts to states
</Card>
Components support natural composition:
<Card>
<CardTitle>Dynamic Title</CardTitle>
<CardBody>Flexible content structure</CardBody>
</Card>
We provide clear, actionable error messages:
let {
className = 'this component needs a class (Component.svelte:10:4)',
ariaLabel = 'this component needs an aria-label',
role = 'this component needs a role',
} = $props();
These messages serve as both errors and documentation, guiding developers toward correct implementation.
States should be used to describe what a component is, not how it looks:
<!-- Good -->
<Card isExpanded={true} />
<!-- Avoid -->
<Card displayStyle="expanded" />
Build complex interfaces through composition rather than configuration:
<!-- Good -->
<Card>
<CardHeader>
<CardTitle>Title</CardTitle>
</CardHeader>
<CardBody>Content</CardBody>
</Card>
<!-- Avoid -->
<Card
headerConfig={{
title: "Title"
}}
bodyContent="Content"
/>
While generic states cover most needs, custom states can be added when necessary:
<script>
let {
children,
...props
} = $props();
const states = $derived({
...GENERIC_MODIFIERS,
isCustomState: props.isCustomState
});
</script>
Styles follow our BEM-inspired naming convention:
.pf-v6-c-card {
// Base styles
&.pf-m-expanded {
// Expanded state styles
}
&__title {
// Title element styles
}
}
Components can be tested straightforwardly:
import { render } from '@testing-library/svelte';
import Card from './Card.svelte';
test('renders with correct classes', () => {
const { container } = render(Card, {
props: {
isPrimary: true
}
});
expect(container.firstChild).toHaveClass('pf-v6-c-card pf-m-primary');
});
Our component system demonstrates that by embracing platform strengths and removing unnecessary constraints, we can create more powerful, flexible, and maintainable solutions. The key is understanding fundamental patterns and working with them rather than against them.
Remember: The best solutions often come not from adding complexity, but from understanding deeply enough to embrace simplicity.