Build a ShadCN UI Button Clone Component Using Vanilla JavaScript
Add to your RSS feed11 November 202416 min readTable of Contents
In this tutorial, we'll create a custom button component that replicates the sleek design and functionality of ShadcnUI buttons using only vanilla JavaScript. We'll implement multiple variants, states, and even add a smooth ripple effect – all without any dependencies. This is perfect for projects where you want the polished look of ShadcnUI but need to stick with vanilla JavaScript.
What We'll Create:
- Multiple button variants (Default, Primary, Secondary, Destructive, Outline, Ghost)
- Interactive states (Hover, Active, Disabled, Loading)
- Different rounded borders
- Sizes of button (sm, md, lg)
- Transform effect animation
- Fully responsive design
- A reusable JavaScript class
Let's dive in and build this component step by step!
We’ll break down this tutorial into three parts:
- Part One: We’ll start by creating a button styled to look like a Shadcn button using only HTML and CSS. Then, we’ll connect this button to our page with a simple JavaScript script.
- Part Two: In this section, we’ll develop a fully functional button component that can be easily used by adding a CSS class directly within any HTML file.
- Part Three: Finally, we’ll create a complete web component. This component will be fully encapsulated and can be integrated simply by adding the
<shadcn-button>click me</shadcn-button>
tag in your HTML.
To make the article more engaging, we’ll apply slightly different button styles in each section.
Part 1: Create a ShadCN-like button
To create a ShadCN UI-style button clone using pure JavaScript, you'll need to replicate the design and interactivity features, such as hover effects, transitions, and focus states. Here's an example of how you can create a simple button with a ShadCN-inspired style using only JavaScript and CSS:
HTML
1 <!DOCTYPE html>2 <html lang="en">3 <head>4 <meta charset="UTF-8">5 <meta name="viewport" content="width=device-width, initial-scale=1.0">6 <title>ShadCN UI Button Clone</title>7 <link rel="stylesheet" href="styles.css">8 </head>9 <body>10 <div id="button-container"></div>11 <script src="script.js"></script>12 </body>13 </html>
CSS (styles.css)
1 /* Base Button Styling */2 .shadcn-btn {3 display: inline-block;4 padding: 12px 24px;5 font-size: 16px;6 font-weight: 600;7 text-align: center;8 text-decoration: none;9 border-radius: 8px;10 background-color: #4CAF50;11 color: white;12 border: 2px solid transparent;13 outline: none;14 transition: all 0.3s ease;15 }1617 /* Hover and Focus Effects */18 .shadcn-btn:hover, .shadcn-btn:focus {19 background-color: #45a049;20 transform: translateY(-2px);21 box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);22 }2324 /* Active Button Effect */25 .shadcn-btn:active {26 background-color: #388e3c;27 transform: translateY(0);28 }2930 /* Disabled Button Effect */31 .shadcn-btn:disabled {32 background-color: #9e9e9e;33 cursor: not-allowed;34 box-shadow: none;35 }
JavaScript (script.js)
1 // Creating the button element2 const buttonContainer = document.getElementById('button-container');3 const button = document.createElement('button');45 // Adding class and text to the button6 button.classList.add('shadcn-btn');7 button.textContent = 'Click Me';89 // Append the button to the container10 buttonContainer.appendChild(button);1112 // Add click event listener (optional functionality)13 button.addEventListener('click', () => {14 alert('Button clicked!');15 });
Explanation:
HTML:
- The HTML contains a
<div>
to hold the button. It references external CSS and JavaScript files.
CSS:
- The
.shadcn-btn
class styles the button to resemble a ShadCN UI button, with rounded corners, padding, and a green background. - The hover, focus, and active states are handled with simple CSS transitions, providing a smooth effect on mouse interaction.
- Disabled buttons have a distinct gray color and are non-interactive.
JavaScript:
- JavaScript dynamically creates a
<button>
element, assigns it the ShadCN button class, and appends it to the DOM. - An event listener is added to show an alert when the button is clicked.
This code creates a button with a ShadCN UI-inspired style and functionality, all without relying on a JavaScript framework or library. You can further customize the styles and behaviors by adjusting the CSS and JavaScript as needed.
Part 2: Create a button component
In this part, we'll build a professional-grade button component that mirrors ShadcnUI's functionality and aesthetics using only vanilla JavaScript.
Setting Up the HTML Structure
1 <!DOCTYPE html>2 <html lang="en">3 <head></head>4 <body>5 <div class="button-container">6 <button class="button button-default">Default Button</button>7 <button class="button button-primary">Primary Button</button>8 <button class="button button-destructive">Destructive Button</button>9 <button class="button button-ghost">Ghost Button</button>10 <button class="button button-primary" disabled>Disabled Button</button>11 <button class="button button-primary button-loading">Loading</button>12 </div>13 </body>
Styling with CSS
Let's add our styles to create the visual foundation:
1 .button-container {2 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;3 padding: 2rem;4 display: flex;5 gap: 1rem;6 flex-wrap: wrap;7 }89 .button {10 display: inline-flex;11 align-items: center;12 justify-content: center;13 border-radius: 0.375rem;14 font-weight: 500;15 font-size: 0.875rem;16 line-height: 1.25rem;17 padding: 0.5rem 1rem;18 cursor: pointer;19 transition: all 0.2s ease;20 position: relative;21 user-select: none;22 }2324 /* Button Variants */25 .button-default {26 background-color: #ffffff;27 color: #000000;28 border: 1px solid #e2e8f0;29 }3031 .button-default:hover {32 background-color: #f8fafc;33 border-color: #cbd5e1;34 }3536 .button-primary {37 background-color: #18181b;38 color: #ffffff;39 border: 1px solid #18181b;40 }4142 .button-primary:hover {43 background-color: #27272a;44 }4546 .button-destructive {47 background-color: #ef4444;48 color: #ffffff;49 border: 1px solid #ef4444;50 }5152 .button-destructive:hover {53 background-color: #dc2626;54 }5556 .button-ghost {57 background-color: transparent;58 border: none;59 color: #000000;60 }6162 .button-ghost:hover {63 background-color: #f1f5f9;64 }
Adding States and Animations
Now let's add styles for different states and the loading animation:
1 /* Disabled state */2 .button:disabled {3 opacity: 0.5;4 cursor: not-allowed;5 pointer-events: none;6 }78 /* Loading state */9 .button-loading {10 position: relative;11 cursor: wait;12 }1314 .button-loading::after {15 content: '';16 position: absolute;17 width: 1rem;18 height: 1rem;19 border: 2px solid transparent;20 border-top-color: currentColor;21 border-right-color: currentColor;22 border-radius: 50%;23 animation: button-spin 0.6s linear infinite;24 margin-left: 0.5rem;25 }2627 @keyframes button-spin {28 from {29 transform: rotate(0deg);30 }31 to {32 transform: rotate(360deg);33 }34 }
JavaScript Implementation
Let's create our button class to handle the functionality:
1 class ShadcnButton {2 constructor(element, options = {}) {3 this.element = element;4 this.options = {5 variant: options.variant || 'default',6 loading: options.loading || false,7 disabled: options.disabled || false,8 onClick: options.onClick || null9 };1011 this.init();12 }1314 init() {15 // Add base class16 this.element.classList.add('button');1718 // Add variant class19 this.element.classList.add(`button-${this.options.variant}`);2021 // Set loading state22 if (this.options.loading) {23 this.setLoading(true);24 }2526 // Set disabled state27 if (this.options.disabled) {28 this.setDisabled(true);29 }3031 // Add click handler32 if (this.options.onClick) {33 this.element.addEventListener('click', this.options.onClick);34 }3536 // Add ripple effect37 this.element.addEventListener('click', (e) => this.createRipple(e));38 }3940 setLoading(loading) {41 if (loading) {42 this.element.classList.add('button-loading');43 this.element.disabled = true;44 } else {45 this.element.classList.remove('button-loading');46 this.element.disabled = this.options.disabled;47 }48 }4950 setDisabled(disabled) {51 this.element.disabled = disabled;52 this.options.disabled = disabled;53 }54 }
Adding the Ripple Effect
Let's implement the ripple animation:
1 createRipple(event) {2 const button = event.currentTarget;3 const ripple = document.createElement('span');45 const diameter = Math.max(button.clientWidth, button.clientHeight);6 const radius = diameter / 2;78 ripple.style.width = ripple.style.height = `${diameter}px`;9 ripple.style.left = `${event.clientX - button.offsetLeft - radius}px`;10 ripple.style.top = `${event.clientY - button.offsetTop - radius}px`;11 ripple.style.position = 'absolute';12 ripple.style.borderRadius = '50%';13 ripple.style.transform = 'scale(0)';14 ripple.style.animation = 'ripple 0.6s linear';15 ripple.style.backgroundColor = 'rgba(255, 255, 255, 0.7)';1617 const style = document.createElement('style');18 style.textContent = `19 @keyframes ripple {20 to {21 transform: scale(4);22 opacity: 0;23 }24 }25 `;2627 document.head.appendChild(style);28 button.appendChild(ripple);2930 setTimeout(() => {31 ripple.remove();32 style.remove();33 }, 600);34 }
Initialization
Finally, let's initialize our buttons:
1 // Initialize all buttons on the page2 document.querySelectorAll('.button').forEach(button => {3 new ShadcnButton(button, {4 variant: button.classList.contains('button-primary') ? 'primary' :5 button.classList.contains('button-destructive') ? 'destructive' :6 button.classList.contains('button-ghost') ? 'ghost' : 'default',7 loading: button.classList.contains('button-loading'),8 disabled: button.disabled,9 onClick: (e) => {10 console.log('Button clicked:', e.target.textContent);11 }12 });13 });
Complete Solution
Check the full implementation in action:
Part 3: Building a Complete Component
Let's start by setting up the structure of our application. We'll create three files: index.html
, style.css
, and app.js
, and fill each with basic markup to get started.
HTML
1 <!DOCTYPE html>2 <html lang="en">3 <head>4 <meta charset="UTF-8">5 <meta name="viewport" content="width=device-width, initial-scale=1.0">6 <title>JS Component</title>7 <link rel="stylesheet" href="style.css">8 </head>9 <body>10 <div class="container">11 </div>12 <script src="app.js"></script>13 </body>14 </html>
CSS
1 * {2 box-sizing: border-box;3 padding: 0;4 margin: 0;5 }6 .container {7 margin-top: 200px;8 }9 .wrapper {10 display: flex;11 align-items: center;12 justify-content: center;13 gap: 24px;14 margin-top: 24px;15 }
Together, these styles create a clean and centered layout for the page content, with evenly spaced items within the wrapper.
JavaScript
Component Basics
1 class Component extends HTMLElement {2 constructor() {3 super();4 this.init(); // Calls init() to initialize shadow DOM and state5 }67 // Initialize the shadow DOM and set up the state object8 init() {9 const shadow = this.attachShadow({ mode: 'open' });10 this.state = {}; // Initializes an empty state object11 }12 }
The Component
class extends HTMLElement
to create a foundation for our custom elements. It initializes an "open" shadow DOM, which encapsulates styles and structure, preventing them from affecting the main document.
The Element.attachShadow() method in JavaScript is used to attach a shadow DOM to an element, which enables encapsulation of HTML, CSS, and JavaScript. This feature is key to building Web Components and helps avoid style and script conflicts between components.
Benefits of attachShadow()
- Encapsulation: Styles and structure inside the shadow DOM are isolated from the rest of the document, which prevents unintended style leaks.
- Reusability: Components can be reused without worrying about conflicts with external styles or scripts.
- Clean Component Boundaries: The method allows building components with their own isolated styles, markup, and behavior, making them self-contained and modular.
In summary, attachShadow()
is crucial for creating robust web components with encapsulation and style isolation, improving the maintainability and scalability of web applications.
Creating the Button Component
1 class Button extends Component {2 constructor() {3 super();4 this._container = document.createElement('button');5 this.data();6 this._style();7 this._html();8 }9 }
This Button
class extends the Component
class, allowing it to reuse the shadow DOM initialization. The constructor:
- Creates the button (
this._container
). - Initializes component data (
this.data()
). - Applies styles and structure (
this._style()
andthis._html()
).
Defining Button Styles
The _style
method defines CSS for button variants, sizes, and styles, including color, padding, and animations.
1 _style() {2 // Create a <style> element to hold the CSS for the button3 const buttonCss = document.createElement('style');4 // Define button background colors, borders, and text colors based on the type5 const variants = {6 default: ['#4c97f8', '#5faefb', 'white'],7 destructive: ['#d6d3d1', '#e7e5e4', 'black'],8 outline: ['#737373', '#a3a3a3', 'black'],9 secondary: ['#10b981', '#34d399', 'white'],10 ghost: ['#f3f4f6', '#f9fafb', 'black'],11 };12 const roundeds = {13 none: '0',14 sm: '2px',15 default: '4px',16 md: '6px',17 lg: '8px',18 xl: '12px',19 '2xl': '16px',20 '3xl': '24px',21 full: '9999px'22 }23 // Style button according to its variant, roundness, and size24 buttonCss.textContent = `25 .btn {26 display: inline-block;27 background: ${variants[this.state.variant][0]};28 border: 2px solid transparent;29 color: ${variants[this.state.variant][2]};30 line-height: 1;31 white-space: nowrap;32 text-align: center;33 box-sizing: border-box;34 padding: 12px 24px;35 font-size: 16px;36 font-weight: 600;37 text-align: center;38 text-decoration: none;39 border-radius: ${roundeds[this.state.rounded]};40 cursor: pointer;41 outline: none;42 transition: all 0.3s ease;43 }44 .btn:hover {45 background: ${variants[this.state.variant][1]};46 transform: translateY(-2px);47 box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);48 }49 .btn-md {50 font-size: 14px;51 padding: 10px 20px;52 }53 .btn-sm {54 font-size: 12px;55 padding: 9px 15px;56 }57 .btn-lg {58 font-size: 16px;59 padding: 12px 24px;60 }61 .btn:disabled {62 opacity: 0.4;63 cursor: not-allowed;64 pointer-events: none;65 }66 .btn-loading {67 position: relative;68 cursor: wait;69 }70 .btn-loading::after {71 content: '';72 position: absolute;73 width: 1rem;74 height: 1rem;75 right: 40%;76 border: 2px solid transparent;77 border-top-color: currentColor;78 border-right-color: currentColor;79 border-radius: 50%;80 animation: button-spin 0.6s linear infinite;81 }8283 @keyframes button-spin {84 from {85 transform: rotate(0deg);86 }87 to {88 transform: rotate(360deg);89 }90 }91 `;92 this.shadowRoot.appendChild(buttonCss); // Append styles to shadow DOM93 }
The buttonCss
variable holds styling rules for various button states:
- Variants: Color schemes for different button types (
default
,destructive
,outline
). - Rounded Corners: Options for rounded button shapes (
none
,sm
,md
,full
). - Loading Spinner: Displays a spinner when loading.
Defining HTML Structure
The _html
method creates the button’s structure and applies classes based on attributes like size and variant.
1 // Create the button's HTML structure2 _html() {3 this._container.classList.add('btn'); // Apply base button class45 if (this.state.size && this.state.size !== 'default') {6 this._container.classList.add(`btn-${this.state.size}`); // Add size class if applicable7 }8 this._container.innerHTML = `<span><slot></slot></span>`; // Add slot for content projection9 this.shadowRoot.appendChild(this._container); // Append the button to shadow DOM10 }
The <slot>
element allows custom content to be placed inside the <shadcn-button>
, making it flexible and reusable.
Getting Attributes and Handling States
Several getAttr
functions retrieve the button's variant
, size
, rounded
, disabled
, and loading
states from its attributes.
1 getAttrVariant() {2 try {3 const variants = ['default', 'destructive', 'outline', 'secondary', 'ghost'];4 let variant = this.getAttribute('variant');5 if (variant && variants.includes(variant)) {6 this.state.variant = variant;7 } else {8 this.state.variant = 'default'; // Default type if invalid9 }10 } catch (e) {11 this.state.variant = 'default'; // Default type if error12 }13 }1415 // Get 'round' attribute value and set it in state16 getAttrRounded() {17 const rounded = this.getAttribute('rounded');18 const roundeds = ['none', 'sm', 'default', 'md', 'lg', 'xl', '2xl', '3xl', 'full'];19 try {20 if (rounded && roundeds.includes(rounded)) {21 this.state.rounded = rounded; // Set roundness based on attribute22 } else {23 this.state.rounded = 'none'; // Default to none24 }25 } catch (e) {26 this.state.rounded = 'none'; // Default to none if error27 }28 }2930 // Get 'size' attribute value and set it in state31 getAttrSize() {32 const size = this.getAttribute('size');33 const sizes = ['md', 'sm', 'lg', 'default'];34 try {35 if (size && sizes.includes(size)) {36 this.state.size = size; // Set size based on attribute37 } else {38 this.state.size = 'default'; // Default to 'default' size if invalid39 }40 } catch (e) {41 this.state.size = 'default'; // Default size if error42 }43 }44 getAttrDisabled() {45 const disabled = this.getAttribute('disabled');46 if (disabled !== null) {47 if (disabled === 'true') {48 this.setDisabled(true);49 } else if (disabled === 'false') {50 this.setDisabled(false);51 }52 }53 }54 getAttrLoading() {55 const loading = this.getAttribute('loading');56 if (loading === "true") {57 this._container.classList.add('btn-loading');58 } else if (loading === "false") {59 this._container.classList.remove('btn-loading');60 }61 }
These functions validate and set the state based on provided attributes. For instance, getAttrDisabled
manages the disabled
state by adding or removing the disabled
attribute on the button element.
To add a setDisabled
method, we'll incorporate logic for handling the button's disabled
state. This method will check if the button should be disabled and then update the component’s state
and actual DOM attributes accordingly.
Here’s how to add setDisabled and how it fits into our overall button component:
Adding setDisabled
for Managing Button State
The setDisabled
function allows dynamic updates to the button's disabled state. This function checks if the disabled
parameter is true
or false
and then updates the button accordingly.
To add a setDisabled method, we'll incorporate logic for handling the button's disabled state. This method will check if the button should be disabled and then update the component’s state and actual DOM attributes accordingly.
Here’s how to add setDisabled and how it fits into our overall button component:
1 setDisabled(disabled) {2 this.state.disabled = disabled;3 if (disabled) {4 this._container.setAttribute('disabled', 'true'); // Adds 'disabled' attribute5 } else {6 this._container.removeAttribute('disabled'); // Removes 'disabled' attribute7 }8 }
If you want to disable or enable <shadcn-button>
dynamically in your app, you could call setDisabled(true)
or setDisabled(false)
in your JavaScript to control the button's availability based on user interaction, loading states, or other events.
Putting It All Together
Finally, customElements.define
registers the <shadcn-button>
component.
1 customElements.define('shadcn-button', Button);
This allows us to use <shadcn-button></shadcn-button>
anywhere in HTML with the functionality, styles, and customizations defined above. By extending HTMLElement
and using shadow DOM, this approach keeps our component modular, customizable, and isolated from global styles.
Full JavaScript Code
1 // The Component class creates a custom HTML element by extending HTMLElement2 class Component extends HTMLElement {3 constructor() {4 super();5 this.init(); // Calls init() to initialize shadow DOM and state6 }78 // Initialize the shadow DOM and set up the state object9 init() {10 const shadow = this.attachShadow({11 mode: 'open', // Creates an open shadow DOM12 });13 this.state = {}; // Initializes an empty state object14 }15 }1617 // <shadcn-button></shadcn-button> is the custom element18 class Button extends Component {19 constructor() {20 super(); // Inherits from Component21 this._container = document.createElement('button'); // Creates the main button element22 this.data(); // Initializes data for the button23 this._style(); // Applies button styles24 this._html(); // Creates the button's HTML structure25 }2627 // Initialize data such as button type, round, and size28 data() {29 this.state = {30 variant: 'default', // Sets default type for button31 rounded: 'none', // Determines if button is rounded32 size: 'default', // Default size of the button33 disabled: false,34 loading: false35 };36 this.getAttrVariant(); // Get type from attributes37 this.getAttrRounded(); // Get roundness from attributes38 this.getAttrSize(); // Get size from attributes39 this.getAttrDisabled();40 this.getAttrLoading();41 }4243 // Add styles to the shadow DOM44 _style() {45 // Create a <style> element to hold the CSS for the button46 const buttonCss = document.createElement('style');47 // Define button background colors, borders, and text colors based on the type48 const variants = {49 default: ['#4c97f8', '#5faefb', 'white'],50 destructive: ['#d6d3d1', '#e7e5e4', 'black'],51 outline: ['#737373', '#a3a3a3', 'black'],52 secondary: ['#10b981', '#34d399', 'white'],53 ghost: ['#f3f4f6', '#f9fafb', 'black'],54 };55 const roundeds = {56 none: '0',57 sm: '2px',58 default: '4px',59 md: '6px',60 lg: '8px',61 xl: '12px',62 '2xl': '16px',63 '3xl': '24px',64 full: '9999px'65 }66 // Style button according to its variant, roundness, and size67 buttonCss.textContent = `68 .btn {69 display: inline-block;70 background: ${variants[this.state.variant][0]};71 border: 2px solid transparent;72 color: ${variants[this.state.variant][2]};73 line-height: 1;74 white-space: nowrap;75 text-align: center;76 box-sizing: border-box;77 padding: 12px 24px;78 font-size: 16px;79 font-weight: 600;80 text-align: center;81 text-decoration: none;82 border-radius: ${roundeds[this.state.rounded]};83 cursor: pointer;84 outline: none;85 transition: all 0.3s ease;86 }87 .btn:hover {88 background: ${variants[this.state.variant][1]};89 transform: translateY(-2px);90 box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);91 }92 .btn-md {93 font-size: 14px;94 padding: 10px 20px;95 }96 .btn-sm {97 font-size: 12px;98 padding: 9px 15px;99 }100 .btn-lg {101 font-size: 16px;102 padding: 12px 24px;103 }104 .btn:disabled {105 opacity: 0.4;106 cursor: not-allowed;107 pointer-events: none;108 }109 .btn-loading {110 position: relative;111 cursor: wait;112 }113 .btn-loading::after {114 content: '';115 position: absolute;116 width: 1rem;117 height: 1rem;118 right: 40%;119 border: 2px solid transparent;120 border-top-color: currentColor;121 border-right-color: currentColor;122 border-radius: 50%;123 animation: button-spin 0.6s linear infinite;124 }125126 @keyframes button-spin {127 from {128 transform: rotate(0deg);129 }130 to {131 transform: rotate(360deg);132 }133 }134 `;135 this.shadowRoot.appendChild(buttonCss); // Append styles to shadow DOM136 }137138 // Create the button's HTML structure139 _html() {140 this._container.classList.add('btn'); // Apply base button class141142 if (this.state.size && this.state.size !== 'default') {143 this._container.classList.add(`btn-${this.state.size}`); // Add size class if applicable144 }145 this._container.innerHTML = `<span><slot></slot></span>`; // Add slot for content projection146 this.shadowRoot.appendChild(this._container); // Append the button to shadow DOM147 }148149 // Get 'type' attribute value and set it in state150 getAttrVariant() {151 try {152 const variants = ['default', 'destructive', 'outline', 'secondary', 'ghost'];153 let variant = this.getAttribute('variant');154 if (variant && variants.includes(variant)) {155 this.state.variant = variant;156 } else {157 this.state.variant = 'default'; // Default type if invalid158 }159 } catch (e) {160 this.state.variant = 'default'; // Default type if error161 }162 }163164 // Get 'round' attribute value and set it in state165 getAttrRounded() {166 const rounded = this.getAttribute('rounded');167 const roundeds = ['none', 'sm', 'default', 'md', 'lg', 'xl', '2xl', '3xl', 'full'];168 try {169 if (rounded && roundeds.includes(rounded)) {170 this.state.rounded = rounded; // Set roundness based on attribute171 } else {172 this.state.rounded = 'none'; // Default to none173 }174 } catch (e) {175 this.state.rounded = 'none'; // Default to none if error176 }177 }178179 // Get 'size' attribute value and set it in state180 getAttrSize() {181 const size = this.getAttribute('size');182 const sizes = ['md', 'sm', 'lg', 'default'];183 try {184 if (size && sizes.includes(size)) {185 this.state.size = size; // Set size based on attribute186 } else {187 this.state.size = 'default'; // Default to 'default' size if invalid188 }189 } catch (e) {190 this.state.size = 'default'; // Default size if error191 }192 }193 getAttrDisabled() {194 const disabled = this.getAttribute('disabled');195 if (disabled !== null) {196 if (disabled === 'true') {197 this.setDisabled(true);198 } else if (disabled === 'false') {199 this.setDisabled(false);200 }201 }202 }203 getAttrLoading() {204 const loading = this.getAttribute('loading');205 if (loading === "true") {206 this._container.classList.add('btn-loading');207 } else if (loading === "false") {208 this._container.classList.remove('btn-loading');209 }210 }211 setDisabled(disabled) {212 this.state.disabled = disabled;213 if (disabled) {214 this._container.setAttribute('disabled', disabled);215 } else {216 this._container.removeAttribute('disabled');217 }218 }219 }220221 // Define the custom element '<Button>'222 customElements.define('shadcn-button', Button);
Result:
Conclusion
By using Web Components, this button component can be customized and reused across different applications. With options for variant
, size
, and rounded
properties, this <shadcn-button>
element provides a robust and flexible button for any UI, offering reusable, styled, and dynamic components without external libraries.