Tailwind CSS Style Integration

This page demonstrates how to use Tailwind CSS to customize links processed by the rehype-smart-links plugin.

Basic Tailwind Integration

The simplest way to use Tailwind with rehype-smart-links is to add Tailwind classes directly:

Basic Tailwind Classes

Internal Link

External Link

Broken Link

JS
// astro.config.mjs
import { defineConfig } from 'astro/config';
import rehypeSmartLinks from 'rehype-smart-links';

export default defineConfig({
  markdown: {
    rehypePlugins: [[
      rehypeSmartLinks, 
      {
        internalLinkClass: 'text-blue-600 hover:text-blue-800 underline',
        externalLinkClass: 'text-purple-600 hover:text-purple-800 underline',
        brokenLinkClass: 'text-red-600 hover:text-red-800 line-through'
      }
    ]]
  }
});

Advanced Tailwind Styling

Create more sophisticated link styles with Tailwind’s utility classes:

Advanced Tailwind Styling

Internal Link

External Link

Broken Link

JS
// astro.config.mjs
import { defineConfig } from 'astro/config';
import rehypeSmartLinks from 'rehype-smart-links';

export default defineConfig({
  markdown: {
    rehypePlugins: [[
      rehypeSmartLinks, 
      {
        internalLinkClass: 'inline-flex items-center px-3 py-1 text-sm rounded-full bg-blue-100 text-blue-700 hover:bg-blue-200 transition-colors duration-200',
        externalLinkClass: 'inline-flex items-center px-3 py-1 text-sm rounded-full bg-purple-100 text-purple-700 hover:bg-purple-200 transition-colors duration-200',
        brokenLinkClass: 'inline-flex items-center px-3 py-1 text-sm rounded-full bg-red-100 text-red-700 line-through opacity-75 cursor-not-allowed'
      }
    ]]
  }
});

Custom Icons with Tailwind

Add icons to your links using Tailwind’s flex utilities:

Links with Tailwind Icons

Internal Link

External Link

Broken Link

JS
// astro.config.mjs
import { defineConfig } from 'astro/config';
import rehypeSmartLinks from 'rehype-smart-links';
import { h } from 'hastscript';

export default defineConfig({
  markdown: {
    rehypePlugins: [[
      rehypeSmartLinks, 
      {
        wrapperTemplate: (node, linkType) => {
          let icon;
          
          if (linkType === 'internal') {
            // Internal link - right arrow
            icon = h('svg', {
              class: 'w-3.5 h-3.5',
              fill: 'none',
              stroke: 'currentColor',
              viewBox: '0 0 24 24',
              xmlns: 'http://www.w3.org/2000/svg'
            }, [
              h('path', {
                'stroke-linecap': 'round',
                'stroke-linejoin': 'round',
                'stroke-width': '2',
                d: 'M13 7l5 5m0 0l-5 5m5-5H6'
              })
            ]);
            
            // Add icon to the end of the link
            node.children.push(icon);
            node.properties.className = [
              'inline-flex',
              'items-center',
              'gap-1',
              'text-blue-600',
              'hover:text-blue-800'
            ];
          } 
          else if (linkType === 'external') {
            // External link - external link icon
            icon = h('svg', {
              class: 'w-3.5 h-3.5',
              fill: 'none',
              stroke: 'currentColor',
              viewBox: '0 0 24 24',
              xmlns: 'http://www.w3.org/2000/svg'
            }, [
              h('path', {
                'stroke-linecap': 'round',
                'stroke-linejoin': 'round',
                'stroke-width': '2',
                d: 'M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14'
              })
            ]);
            
            // Add icon to the end of the link
            node.children.push(icon);
            node.properties.className = [
              'inline-flex',
              'items-center',
              'gap-1',
              'text-purple-600',
              'hover:text-purple-800'
            ];
          } 
          else if (linkType === 'broken') {
            // Broken link - warning icon
            icon = h('svg', {
              class: 'w-3.5 h-3.5',
              fill: 'none',
              stroke: 'currentColor',
              viewBox: '0 0 24 24',
              xmlns: 'http://www.w3.org/2000/svg'
            }, [
              h('path', {
                'stroke-linecap': 'round',
                'stroke-linejoin': 'round',
                'stroke-width': '2',
                d: 'M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z'
              })
            ]);
            
            // Add icon to the beginning of the link
            node.children = [icon, ...node.children];
            node.properties.className = [
              'inline-flex',
              'items-center',
              'gap-1',
              'text-red-600',
              'hover:text-red-800'
            ];
          }
          
          return node;
        }
      }
    ]]
  }
});

Create eye-catching animations for your links using Tailwind’s transition utilities:

Animated Links with Tailwind

Internal Link

External Link

Broken Link

JS
// astro.config.mjs
import { defineConfig } from 'astro/config';
import rehypeSmartLinks from 'rehype-smart-links';
import { h } from 'hastscript';

export default defineConfig({
  markdown: {
    rehypePlugins: [[
      rehypeSmartLinks, 
      {
        wrapperTemplate: (node, linkType) => {
          if (linkType === 'internal') {
            // Store original text
            const originalText = node.children[0]?.value || 'Link';
            
            // Create underline animation effect
            node.children = [
              h('span', { class: 'relative z-10' }, [originalText]),
              h('span', { 
                class: 'absolute bottom-0 left-0 w-full h-0.5 bg-blue-600 origin-left transform scale-x-0 transition-transform duration-300 ease-out group-hover:scale-x-100'
              })
            ];
            
            node.properties.className = [
              'relative',
              'text-blue-600',
              'font-medium'
            ];
          } 
          else if (linkType === 'external') {
            // Arrow icon with animation
            const icon = h('svg', {
              class: 'w-3.5 h-3.5 transform transition-transform duration-300 group-hover:translate-x-1',
              fill: 'none',
              stroke: 'currentColor',
              viewBox: '0 0 24 24',
              xmlns: 'http://www.w3.org/2000/svg'
            }, [
              h('path', {
                'stroke-linecap': 'round',
                'stroke-linejoin': 'round',
                'stroke-width': '2',
                d: 'M14 5l7 7m0 0l-7 7m7-7H3'
              })
            ]);
            
            node.children.push(icon);
            node.properties.className = [
              'group',
              'inline-flex',
              'items-center',
              'gap-1',
              'text-purple-600',
              'font-medium'
            ];
          } 
          else if (linkType === 'broken') {
            // Warning icon with animation
            const text = node.children[0]?.value || 'Broken Link';
            const icon = h('svg', {
              class: 'inline-block w-4 h-4 ml-1 animate-pulse',
              fill: 'none',
              stroke: 'currentColor',
              viewBox: '0 0 24 24',
              xmlns: 'http://www.w3.org/2000/svg'
            }, [
              h('path', {
                'stroke-linecap': 'round',
                'stroke-linejoin': 'round',
                'stroke-width': '2',
                d: 'M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z'
              })
            ]);
            
            node.children = [
              h('span', { class: 'opacity-75' }, [text]),
              icon
            ];
            
            node.properties.className = [
              'relative',
              'text-red-600',
              'font-medium',
              'cursor-not-allowed'
            ];
          }
          
          return node;
        }
      }
    ]]
  }
});

Create card-style links for more prominent navigation options:

Tailwind Card Links

JS
// astro.config.mjs
import { defineConfig } from 'astro/config';
import rehypeSmartLinks from 'rehype-smart-links';
import { h } from 'hastscript';

export default defineConfig({
  markdown: {
    rehypePlugins: [[
      rehypeSmartLinks, 
      {
        wrapperTemplate: (node, linkType) => {
          const originalText = node.children[0]?.value || 'Link';
          let icon, container, title, description;
          
          if (linkType === 'internal') {
            // Info icon for internal link
            icon = h('svg', {
              class: 'w-5 h-5 text-blue-600',
              fill: 'none',
              stroke: 'currentColor',
              viewBox: '0 0 24 24',
              xmlns: 'http://www.w3.org/2000/svg'
            }, [
              h('path', {
                'stroke-linecap': 'round',
                'stroke-linejoin': 'round',
                'stroke-width': '2',
                d: 'M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z'
              })
            ]);
            
            container = h('div', { class: 'flex-shrink-0 bg-blue-100 p-2 rounded-lg' }, [icon]);
            title = h('h3', { class: 'text-sm font-medium text-gray-900' }, [originalText]);
            description = h('p', { class: 'text-xs text-gray-500' }, ['Learn more about our project']);
            
            node.properties.className = [
              'block',
              'p-4',
              'bg-white',
              'border',
              'border-gray-200',
              'rounded-lg',
              'shadow-sm',
              'hover:bg-gray-50',
              'transition-colors',
              'duration-200'
            ];
          } 
          else if (linkType === 'external') {
            // External link icon
            icon = h('svg', {
              class: 'w-5 h-5 text-purple-600',
              fill: 'none',
              stroke: 'currentColor',
              viewBox: '0 0 24 24',
              xmlns: 'http://www.w3.org/2000/svg'
            }, [
              h('path', {
                'stroke-linecap': 'round',
                'stroke-linejoin': 'round',
                'stroke-width': '2',
                d: 'M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14'
              })
            ]);
            
            container = h('div', { class: 'flex-shrink-0 bg-purple-100 p-2 rounded-lg' }, [icon]);
            title = h('h3', { class: 'text-sm font-medium text-gray-900' }, [originalText]);
            description = h('p', { class: 'text-xs text-gray-500' }, ['View source code (opens in new tab)']);
            
            node.properties.className = [
              'block',
              'p-4',
              'bg-white',
              'border',
              'border-gray-200',
              'rounded-lg',
              'shadow-sm',
              'hover:bg-gray-50',
              'transition-colors',
              'duration-200'
            ];
          } 
          else if (linkType === 'broken') {
            // Warning icon for broken link
            icon = h('svg', {
              class: 'w-5 h-5 text-red-600',
              fill: 'none',
              stroke: 'currentColor',
              viewBox: '0 0 24 24',
              xmlns: 'http://www.w3.org/2000/svg'
            }, [
              h('path', {
                'stroke-linecap': 'round',
                'stroke-linejoin': 'round',
                'stroke-width': '2',
                d: 'M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z'
              })
            ]);
            
            container = h('div', { class: 'flex-shrink-0 bg-red-100 p-2 rounded-lg' }, [icon]);
            title = h('h3', { class: 'text-sm font-medium text-gray-900 line-through' }, [originalText]);
            description = h('p', { class: 'text-xs text-red-500' }, ['This page does not exist']);
            
            node.properties.className = [
              'block',
              'p-4',
              'bg-white',
              'border',
              'border-gray-200',
              'rounded-lg',
              'shadow-sm',
              'opacity-75',
              'cursor-not-allowed'
            ];
          }
          
          // Create content container
          const content = h('div', { class: 'ml-4' }, [title, description]);
          
          // Create flexible layout
          const flex = h('div', { class: 'flex items-center' }, [container, content]);
          
          // Update node children
          node.children = [flex];
          
          return node;
        }
      }
    ]]
  }
});

Dark Mode Support

Create links that work well in both light and dark modes with Tailwind’s dark mode utilities:

Dark Mode Compatible Links

Internal Link

External Link

Broken Link

JS
// tailwind.config.js
module.exports = {
  darkMode: 'class', // or 'media' if you prefer system preferences
  // other tailwind configuration...
}

// astro.config.mjs
import { defineConfig } from 'astro/config';
import rehypeSmartLinks from 'rehype-smart-links';

export default defineConfig({
  markdown: {
    rehypePlugins: [[
      rehypeSmartLinks, 
      {
        className: {
          internal: 'text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 underline',
          external: 'text-purple-600 dark:text-purple-400 hover:text-purple-800 dark:hover:text-purple-300 underline',
          broken: 'text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300 line-through'
        }
      }
    ]]
  }
});

Tailwind for Tooltips

Use Tailwind to create custom tooltips for your links:

Tailwind Tooltip Links

Internal Link

About Page

External Link

GitHub

Broken Link

Broken Link
JS
// astro.config.mjs
import { defineConfig } from 'astro/config';
import rehypeSmartLinks from 'rehype-smart-links';
import { h } from 'hastscript';

export default defineConfig({
  markdown: {
    rehypePlugins: [[
      rehypeSmartLinks, 
      {
        wrapperTemplate: (node, linkType) => {
          // Set link styles based on type
          if (linkType === 'internal') {
            node.properties.className = ['text-blue-600', 'hover:text-blue-800', 'group-hover:underline'];
            
            // Create tooltip
            const tooltip = h('span', {
              class: 'invisible group-hover:visible absolute left-1/2 -translate-x-1/2 -bottom-9 w-28 px-2 py-1 bg-gray-900 rounded-md text-center text-white text-xs after:content-[""] after:absolute after:left-1/2 after:-top-1 after:-translate-x-1/2 after:border-4 after:border-transparent after:border-b-gray-900'
            }, ['Internal page']);
            
            // Wrap in container with tooltip
            return h('span', { class: 'group relative' }, [node, tooltip]);
          } 
          else if (linkType === 'external') {
            node.properties.className = ['text-purple-600', 'hover:text-purple-800', 'group-hover:underline'];
            
            // Create tooltip
            const tooltip = h('span', {
              class: 'invisible group-hover:visible absolute left-1/2 -translate-x-1/2 -bottom-9 w-32 px-2 py-1 bg-gray-900 rounded-md text-center text-white text-xs after:content-[""] after:absolute after:left-1/2 after:-top-1 after:-translate-x-1/2 after:border-4 after:border-transparent after:border-b-gray-900'
            }, ['Opens in new tab']);
            
            // Wrap in container with tooltip
            return h('span', { class: 'group relative' }, [node, tooltip]);
          } 
          else if (linkType === 'broken') {
            node.properties.className = ['text-red-600', 'hover:text-red-800', 'line-through', 'cursor-not-allowed'];
            
            // Create tooltip
            const tooltip = h('span', {
              class: 'invisible group-hover:visible absolute left-1/2 -translate-x-1/2 -bottom-9 w-28 px-2 py-1 bg-gray-900 rounded-md text-center text-white text-xs after:content-[""] after:absolute after:left-1/2 after:-top-1 after:-translate-x-1/2 after:border-4 after:border-transparent after:border-b-gray-900'
            }, ['Link not found']);
            
            // Wrap in container with tooltip
            return h('span', { class: 'group relative' }, [node, tooltip]);
          }
          
          return node;
        }
      }
    ]]
  }
});

Next Steps

Explore more styling options:

Related Links Astro Tailwind CSS DaisyUI
Resources GitHub NPM