Display
Tag
Etiqueta con logo y nombre, para tecnologías y herramientas
Preview
size
icon
children
Code
import css from "./Tag.module.css";
type Size = "sm" | "md";
interface ResponsiveSize {
base: Size;
mobile?: Size;
tablet?: Size;
desktop?: Size;
wide?: Size;
}
interface TagProps {
size: Size | ResponsiveSize;
icon: string;
children: React.ReactNode;
}
function getSizeClasses(size: Size | ResponsiveSize): string {
if (typeof size === "string") {
return css[size];
}
const classes = [css[`${size.base}_base`]];
if (size.mobile) classes.push(css[`${size.mobile}_mobile`]);
if (size.tablet) classes.push(css[`${size.tablet}_tablet`]);
if (size.desktop) classes.push(css[`${size.desktop}_desktop`]);
if (size.wide) classes.push(css[`${size.wide}_wide`]);
return classes.join(" ");
}
export default function Tag({
size,
icon,
children,
}: TagProps) {
return (
<span
className={`
${css.tag}
${getSizeClasses(size)}
`}
>
<img src={icon} alt="" className={css.tag_icon} />
{children}
</span>
);
}
.tag {
display: inline-flex;
align-items: center;
height: var(--tag-height);
border-radius: var(--radius-8);
background: color-mix(in srgb, var(--color-text) 6%, transparent);
font-weight: var(--weight-500);
/* The text side takes 4 more than the logo side, so both ends read even. */
&.sm {
--tag-height: var(--size-32);
--tag-icon: 20px;
gap: var(--space-8);
padding: 0 var(--space-12) 0 var(--space-8);
font-size: var(--text-14);
}
&.md {
--tag-height: var(--size-40);
--tag-icon: 24px;
gap: var(--space-12);
padding: 0 var(--space-16) 0 var(--space-12);
font-size: var(--text-14);
}
&.sm_base {
--tag-height: var(--size-32);
--tag-icon: 20px;
gap: var(--space-8);
padding: 0 var(--space-12) 0 var(--space-8);
font-size: var(--text-14);
}
&.md_base {
--tag-height: var(--size-40);
--tag-icon: 24px;
gap: var(--space-12);
padding: 0 var(--space-16) 0 var(--space-12);
font-size: var(--text-14);
}
@media (min-width: 480px) {
&.sm_mobile {
--tag-height: var(--size-32);
--tag-icon: 20px;
gap: var(--space-8);
padding: 0 var(--space-12) 0 var(--space-8);
font-size: var(--text-14);
}
&.md_mobile {
--tag-height: var(--size-40);
--tag-icon: 24px;
gap: var(--space-12);
padding: 0 var(--space-16) 0 var(--space-12);
font-size: var(--text-14);
}
}
@media (min-width: 768px) {
&.sm_tablet {
--tag-height: var(--size-32);
--tag-icon: 20px;
gap: var(--space-8);
padding: 0 var(--space-12) 0 var(--space-8);
font-size: var(--text-14);
}
&.md_tablet {
--tag-height: var(--size-40);
--tag-icon: 24px;
gap: var(--space-12);
padding: 0 var(--space-16) 0 var(--space-12);
font-size: var(--text-14);
}
}
@media (min-width: 1024px) {
&.sm_desktop {
--tag-height: var(--size-32);
--tag-icon: 20px;
gap: var(--space-8);
padding: 0 var(--space-12) 0 var(--space-8);
font-size: var(--text-14);
}
&.md_desktop {
--tag-height: var(--size-40);
--tag-icon: 24px;
gap: var(--space-12);
padding: 0 var(--space-16) 0 var(--space-12);
font-size: var(--text-14);
}
}
@media (min-width: 1440px) {
&.sm_wide {
--tag-height: var(--size-32);
--tag-icon: 20px;
gap: var(--space-8);
padding: 0 var(--space-12) 0 var(--space-8);
font-size: var(--text-14);
}
&.md_wide {
--tag-height: var(--size-40);
--tag-icon: 24px;
gap: var(--space-12);
padding: 0 var(--space-16) 0 var(--space-12);
font-size: var(--text-14);
}
}
.tag_icon {
width: var(--tag-icon);
height: var(--tag-icon);
object-fit: contain;
}
}
<script setup lang="ts">
import { computed } from "vue";
type Size = "sm" | "md";
interface ResponsiveSize {
base: Size;
mobile?: Size;
tablet?: Size;
desktop?: Size;
wide?: Size;
}
interface TagProps {
size: Size | ResponsiveSize;
icon: string;
}
const { size } = defineProps<TagProps>();
const sizeClasses = computed(() => {
if (typeof size === "string") {
return size;
}
const classes = [`${size.base}_base`];
if (size.mobile) classes.push(`${size.mobile}_mobile`);
if (size.tablet) classes.push(`${size.tablet}_tablet`);
if (size.desktop) classes.push(`${size.desktop}_desktop`);
if (size.wide) classes.push(`${size.wide}_wide`);
return classes.join(" ");
});
</script>
<template>
<span class="tag" :class="sizeClasses">
<img :src="icon" alt="" class="tag_icon" />
<slot></slot>
</span>
</template>
<style scoped>
.tag {
display: inline-flex;
align-items: center;
height: var(--tag-height);
border-radius: var(--radius-8);
background: color-mix(in srgb, var(--color-text) 6%, transparent);
font-weight: var(--weight-500);
/* The text side takes 4 more than the logo side, so both ends read even. */
&.sm {
--tag-height: var(--size-32);
--tag-icon: 20px;
gap: var(--space-8);
padding: 0 var(--space-12) 0 var(--space-8);
font-size: var(--text-14);
}
&.md {
--tag-height: var(--size-40);
--tag-icon: 24px;
gap: var(--space-12);
padding: 0 var(--space-16) 0 var(--space-12);
font-size: var(--text-14);
}
&.sm_base {
--tag-height: var(--size-32);
--tag-icon: 20px;
gap: var(--space-8);
padding: 0 var(--space-12) 0 var(--space-8);
font-size: var(--text-14);
}
&.md_base {
--tag-height: var(--size-40);
--tag-icon: 24px;
gap: var(--space-12);
padding: 0 var(--space-16) 0 var(--space-12);
font-size: var(--text-14);
}
@media (min-width: 480px) {
&.sm_mobile {
--tag-height: var(--size-32);
--tag-icon: 20px;
gap: var(--space-8);
padding: 0 var(--space-12) 0 var(--space-8);
font-size: var(--text-14);
}
&.md_mobile {
--tag-height: var(--size-40);
--tag-icon: 24px;
gap: var(--space-12);
padding: 0 var(--space-16) 0 var(--space-12);
font-size: var(--text-14);
}
}
@media (min-width: 768px) {
&.sm_tablet {
--tag-height: var(--size-32);
--tag-icon: 20px;
gap: var(--space-8);
padding: 0 var(--space-12) 0 var(--space-8);
font-size: var(--text-14);
}
&.md_tablet {
--tag-height: var(--size-40);
--tag-icon: 24px;
gap: var(--space-12);
padding: 0 var(--space-16) 0 var(--space-12);
font-size: var(--text-14);
}
}
@media (min-width: 1024px) {
&.sm_desktop {
--tag-height: var(--size-32);
--tag-icon: 20px;
gap: var(--space-8);
padding: 0 var(--space-12) 0 var(--space-8);
font-size: var(--text-14);
}
&.md_desktop {
--tag-height: var(--size-40);
--tag-icon: 24px;
gap: var(--space-12);
padding: 0 var(--space-16) 0 var(--space-12);
font-size: var(--text-14);
}
}
@media (min-width: 1440px) {
&.sm_wide {
--tag-height: var(--size-32);
--tag-icon: 20px;
gap: var(--space-8);
padding: 0 var(--space-12) 0 var(--space-8);
font-size: var(--text-14);
}
&.md_wide {
--tag-height: var(--size-40);
--tag-icon: 24px;
gap: var(--space-12);
padding: 0 var(--space-16) 0 var(--space-12);
font-size: var(--text-14);
}
}
.tag_icon {
width: var(--tag-icon);
height: var(--tag-icon);
object-fit: contain;
}
}
</style>
Props
| Prop | Type | Required | Description |
|---|---|---|---|
| size | "sm" | "md" | ResponsiveSize | required | Tamaño de la etiqueta (altura, icono y padding). Fijo o responsive. |
| icon | string | required | Ruta de la imagen del icono. |
| children | React.ReactNode | required | Contenido de la etiqueta. |