How the Pill-Shaped Activity-Bar Selection Indicator Works in Islands Dark
The pill-shaped selection indicator in the Islands Dark VS Code theme is created purely with CSS keyframe animations that morph a flat element into a rounded pill and add a subtle breathing glow effect.
The Islands Dark theme for VS Code features a distinctive pill-shaped selection indicator on the activity bar that animates when you switch between views like Explorer or Search. Unlike traditional bitmap icons, this visual effect is generated entirely through CSS animations defined in the theme's animations.css file. Understanding how this pill-shaped activity bar selection indicator works reveals a clever combination of transform scaling, border-radius manipulation, and filter shadows that responds to VS Code's internal state classes.
What Triggers the Animation
When a view becomes active in VS Code, the application automatically adds the class action-item checked to the corresponding activity-bar button. The Islands Dark theme targets this specific state using the selector .part.activitybar .action-item.checked .action-label defined in animations.css at lines 127-131. This CSS selector catches the checked state and applies the animation sequence to the label element inside the activity bar item.
The Drop-Morph Animation
The primary animation that creates the pill shape is defined in animations.css at lines 20-38. The @keyframes islands-drop-morph sequence morphs a flat element into a pill-shaped rounded rectangle through three simultaneous transformations:
- Scale transformation – Expands the element horizontally while shrinking it vertically to create the characteristic pill proportions
- Opacity fade – Brings the element from invisible to fully visible during the first 20% of the animation
- Border-radius expansion – Increases the corner radius from 35% to 50%, transforming a rectangle into a perfect ellipse
@keyframes islands-drop-morph {
0% { transform: scaleX(0.45) scaleY(2); opacity: 0; border-radius: 35%; }
20% { opacity: 0.7; border-radius: 40%; transform: scaleX(0.5) scaleY(1.5); }
50% { transform: scaleX(1.2) scaleY(0.8); opacity: 1; border-radius: 50%; }
70% { transform: scaleX(0.92) scaleY(1.08); }
85% { transform: scaleX(1.04) scaleY(0.96); }
100% { transform: scaleX(1) scaleY(1); opacity: 1; border-radius: 50%; }
}
The Breathing Glow Effect
After the initial morph completes, a secondary animation add a subtle "breathing" glow. Defined in animations.css at lines 44-48, the islands-breathe keyframes animate a drop-shadow filter to create a pulsing accent effect:
@keyframes islands-breathe {
0%, 100% { filter: drop-shadow(0 0 2px rgba(13,188,121,0)); }
50% { filter: drop-shadow(0 0 8px rgba(13,188,121,0.3)); }
}
This animation uses the theme's accent color (defined in themes/islands-dark.json as #548af7) to create a soft glow that pulses indefinitely.
Combining the Animations
The final effect is achieved by applying both animations simultaneously to the checked label. As shown in animations.css at lines 127-131, the CSS declaration combines the morph and breathe animations with specific timing functions:
.part.activitybar .action-item.checked .action-label {
animation:
islands-drop-morph 0.4s cubic-bezier(0.22, 1, 0.36, 1),
islands-breathe 4s ease-in-out 0.5s infinite !important;
}
The islands-drop-morph runs once over 0.4 seconds using a custom cubic-bezier curve for smooth settling, while islands-breathe starts after a 0.5-second delay and loops infinitely every 4 seconds.
Working Implementation Example
You can reproduce this effect outside of VS Code using the following minimal HTML and CSS. This snippet demonstrates how the islands-drop-morph and islands-breathe animations work together to create the pill-shaped indicator:
<style>
@keyframes islands-drop-morph {
0% { transform: scaleX(0.45) scaleY(2); opacity: 0; border-radius: 35%; }
20% { opacity: 0.7; border-radius: 40%; transform: scaleX(0.5) scaleY(1.5); }
50% { transform: scaleX(1.2) scaleY(0.8); opacity: 1; border-radius: 50%; }
70% { transform: scaleX(0.92) scaleY(1.08); }
85% { transform: scaleX(1.04) scaleY(0.96); }
100% { transform: scaleX(1) scaleY(1); opacity: 1; border-radius: 50%; }
}
@keyframes islands-breathe {
0%, 100% { filter: drop-shadow(0 0 2px rgba(13,188,121,0)); }
50% { filter: drop-shadow(0 0 8px rgba(13,188,121,0.3)); }
}
.activity-bar .item.checked .label {
display: inline-block;
padding: 4px 12px;
background: #548af7;
color: #fff;
border-radius: 50%;
animation:
islands-drop-morph 0.4s cubic-bezier(0.22, 1, 0.36, 1),
islands-breathe 4s ease-in-out 0.5s infinite;
}
</style>
<div class="activity-bar">
<div class="item checked"><span class="label">Explorer</span></div>
</div>
Summary
- Pure CSS implementation – The pill-shaped activity bar selection indicator relies entirely on CSS keyframe animations without any image assets
- Two-stage animation – The effect combines an initial
islands-drop-morphanimation that creates the shape, followed by an infiniteislands-breatheanimation for the glow effect - State-based triggering – VS Code adds the
action-item checkedclass automatically when a view is active, which triggers the CSS selector inanimations.css - Responsive design – Because the animation uses scalable CSS properties (transform, border-radius, filter), the pill shape adapts to any DPI or zoom level automatically
- Source locations – Key definitions reside in
animations.css(lines 20-38 for the morph, lines 44-48 for the breathe, and lines 127-131 for the application rule)
Frequently Asked Questions
How does the pill shape form without using images?
The pill shape is created dynamically using the border-radius CSS property animated from 35% to 50% combined with transform: scaleX() and scaleY() operations. As the islands-drop-morph animation progresses, the element stretches horizontally while its corners become increasingly rounded, resulting in a perfect pill shape by the animation's end.
Where are the animation keyframes defined in the repository?
All animation logic resides in the animations.css file. The @keyframes islands-drop-morph definition spans lines 20-38, while @keyframes islands-breathe appears at lines 44-48. The selector that applies these animations to the activity bar is located at lines 127-131 in the same file.
Can I customize the animation speed or disable the breathing effect?
Yes. You can override the animation duration by modifying the 0.4s value for islands-drop-morph or the 4s value for islands-breathe in your VS Code settings.json using custom CSS injection. To disable the breathing effect entirely, remove the islands-breathe reference from the animation declaration, leaving only the morph animation for a static pill indicator.
What triggers the animation to start when I click an activity bar icon?
VS Code's core UI automatically adds the checked class to the action-item div when its associated view becomes active. The Islands Dark theme leverages this standard VS Code behavior by targeting .part.activitybar .action-item.checked .action-label, which means the animation starts immediately when VS Code updates the DOM class, requiring no JavaScript or additional markup from the theme itself.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →