Drill Transition

Drill forward/back transition for hierarchical navigation

Drill Transition

The drill transition clearly expresses navigation between levels of information hierarchy. It creates an effect where screens slide sideways as users enter or exit new levels, intuitively conveying their current position and movement direction.

Demo

Loading demo...

UX Principles

When to Use

Drill transition is optimized for parent-child relationships and hierarchical navigation.

Suitability by Content Relationship

콘텐츠 관계적합성설명
Unrelated contentNot suitable for independent sections
Sibling contentUnnatural between same-level content
Hierarchical contentOptimal for list→sublist, overview→detail

Primary Use Cases

  • List → Sublist: Moving from category to subcategory
  • Overview → Detail: Entering detailed analysis from dashboard
  • Settings → Sub-settings: Moving from main settings to specific options
  • File Explorer: Navigating folder structures

Why Does It Work This Way?

  1. Spatial Metaphor: Sliding motion represents "entering" and "exiting"
  2. Hierarchy Recognition: Overlapping screens visualize information depth
  3. Gesture-Friendly: Naturally integrates with mobile edge-swipe gestures
  4. Context Preservation: Previous screen partially visible for path awareness

Motion Design

Drill Forward (Enter):
1. New screen → Starts at 100% position from right
2. During motion → New screen enters, pushing existing screen
3. Complete → New screen takes full space, existing at -20%

Drill Back (Exit):
1. Current screen → Starts at 0% position
2. During motion → Slides right, revealing previous screen
3. Complete → Previous screen restored, current at 100% out

Basic Usage

1. Transition Configuration

import { Ssgoi } from '@ssgoi/react';
import { drill } from '@ssgoi/react/view-transitions';

const config = {
  transitions: [
    {
      from: '/categories',
      to: '/categories/*',
      transition: drill({ direction: 'enter' }),
      symmetric: false
    },
    {
      from: '/categories/*',
      to: '/categories',
      transition: drill({ direction: 'exit' })
    }
  ]
};

export default function App() {
  return (
    <Ssgoi config={config}>
      {/* App content */}
    </Ssgoi>
  );
}

2. Options Configuration

interface DrillOptions {
  opacity?: boolean;           // Opacity effect (default: false)
  direction?: 'enter' | 'exit'; // Drill direction (default: 'enter')
  spring?: {
    stiffness?: number;         // Spring stiffness (default: 150)
    damping?: number;           // Damping coefficient (default: 20)
  };
}

Practical Examples

1. Category Navigation

Navigating nested category structures:

// Main Categories
function Categories() {
  return (
    <div className="p-4">
      <h1 className="text-2xl mb-4">Categories</h1>
      <div className="space-y-2">
        {categories.map(cat => (
          <Link
            key={cat.id}
            to={`/categories/${cat.slug}`}
            className="block p-4 bg-white rounded-lg shadow"
          >
            <h3 className="font-semibold">{cat.name}</h3>
            <p className="text-gray-600">{cat.itemCount} items</p>
          </Link>
        ))}
      </div>
    </div>
  );
}

// Subcategory
function SubCategory({ category }) {
  return (
    <div className="p-4">
      <button onClick={() => navigate('/categories')} className="mb-4">
        ← Back
      </button>
      <h1 className="text-2xl mb-4">{category.name}</h1>
      <div className="grid gap-4">
        {category.items.map(item => (
          <div key={item.id} className="p-4 bg-white rounded-lg">
            {item.name}
          </div>
        ))}
      </div>
    </div>
  );
}

2. Settings Menu

From settings to detailed options:

// Main Settings
function Settings() {
  const menuItems = [
    { id: 'profile', label: 'Profile Settings', icon: '👤' },
    { id: 'privacy', label: 'Privacy & Security', icon: '🔒' },
    { id: 'notifications', label: 'Notifications', icon: '🔔' },
    { id: 'display', label: 'Display Settings', icon: '🎨' }
  ];

  return (
    <div className="max-w-lg mx-auto">
      <h1 className="text-2xl p-4 border-b">Settings</h1>
      <div className="divide-y">
        {menuItems.map(item => (
          <Link
            key={item.id}
            to={`/settings/${item.id}`}
            className="flex items-center p-4 hover:bg-gray-50"
          >
            <span className="text-2xl mr-4">{item.icon}</span>
            <span className="flex-1">{item.label}</span>
            <span></span>
          </Link>
        ))}
      </div>
    </div>
  );
}

// Setting Detail
function SettingDetail({ type }) {
  return (
    <div className="max-w-lg mx-auto">
      <div className="flex items-center p-4 border-b">
        <button onClick={() => navigate('/settings')}>←</button>
        <h1 className="text-xl ml-4">{getSettingTitle(type)}</h1>
      </div>
      <div className="p-4">
        {/* Detailed setting options */}
      </div>
    </div>
  );
}

3. File Explorer

Folder structure navigation:

function FileExplorer({ path }) {
  const config = {
    transitions: [
      {
        from: '/files/*',
        to: '/files/*/*',
        transition: drill({ 
          opacity: true,
          spring: { stiffness: 180, damping: 22 }
        })
      }
    ]
  };

  return (
    <div className="h-screen flex flex-col">
      <div className="p-3 bg-gray-100 text-sm">
        {path.split('/').map((segment, i) => (
          <span key={i}>
            {i > 0 && ' / '}
            <button onClick={() => navigateToLevel(i)}>
              {segment}
            </button>
          </span>
        ))}
      </div>
      <div className="flex-1 overflow-auto p-4">
        {files.map(file => (
          <div
            key={file.id}
            onClick={() => file.isFolder && navigate(file.path)}
            className="flex items-center p-2 hover:bg-gray-50"
          >
            <span className="mr-2">
              {file.isFolder ? '📁' : '📄'}
            </span>
            {file.name}
          </div>
        ))}
      </div>
    </div>
  );
}

Advanced Configuration

Adding Opacity Effect

// Content fades while drilling
drill({ 
  opacity: true,
  spring: { stiffness: 120, damping: 18 }
})

Direction Customization

// Drill in (default)
drill({ direction: 'enter' })

// Drill out (going back)
drill({ direction: 'exit' })

Spring Physics Adjustment

// Fast drill
drill({ 
  spring: { stiffness: 200, damping: 25 }
})

// Smooth drill
drill({ 
  spring: { stiffness: 100, damping: 15 }
})

Considerations

Hierarchy Consistency

  • Use drill transition only with clear hierarchical structures
  • Use slide or fade for same-level navigation
  • Apply reverse drill direction when going back

Performance Considerations

  • Optimize transforms for large images or complex layouts
  • Use will-change property on mobile
  • Avoid animating too many elements simultaneously

Accessibility

  • Instant transition with reduced motion preference
  • Full keyboard navigation support
  • Recognized as standard page transition by screen readers
  • Focus automatically moves to new page

Best Practices

✅ DO

  • Use for clear parent-child relationships
  • Integrate with back gestures
  • Display path with breadcrumbs
  • Maintain consistent directionality

❌ DON'T

  • Don't use between sibling or independent sections
  • Don't set speed too fast
  • Don't use bidirectional drill simultaneously
  • Don't overuse in deep hierarchies (>5 levels)