diff --git a/docs/.vitepress/data/teamData.json b/docs/.vitepress/data/teamData.json index 4f46f57f1..07cf6a1cb 100644 --- a/docs/.vitepress/data/teamData.json +++ b/docs/.vitepress/data/teamData.json @@ -1,7 +1,7 @@ [ { "name": "Eric Fennis", - "title": "Creator of Lucide & Software engineer @nedap", + "title": "Creator & Maintainer of Lucide", "image": "https://github.com/ericfennis.png?size=192", "sponsor": "https://github.com/sponsors/ericfennis", "socialLinks": [ @@ -12,6 +12,10 @@ { "icon": "x", "link": "https://x.com/ericfennis" + }, + { + "icon": "linkedin", + "link": "https://www.linkedin.com/in/ericfennis" } ] }, @@ -32,7 +36,7 @@ }, { "name": "Jakob Guddas", - "title": "Maintainer of Lucide & Software engineer @LEGO", + "title": "Creator of Lucide Studio", "image": "https://github.com/jguddas.png?size=192", "socialLinks": [ { diff --git a/docs/.vitepress/theme/components/base/Card.vue b/docs/.vitepress/theme/components/base/Card.vue index 23500f5a6..03d471851 100644 --- a/docs/.vitepress/theme/components/base/Card.vue +++ b/docs/.vitepress/theme/components/base/Card.vue @@ -31,7 +31,7 @@ const rel = computed(() => { diff --git a/docs/.vitepress/theme/components/base/Checkbox.vue b/docs/.vitepress/theme/components/base/Checkbox.vue index a13e9469a..058d9fdf5 100644 --- a/docs/.vitepress/theme/components/base/Checkbox.vue +++ b/docs/.vitepress/theme/components/base/Checkbox.vue @@ -79,6 +79,15 @@ const model = computed({ border: 1px solid var(--vp-input-border-color); background-color: var(--vp-input-switch-bg-color); border-radius: 4px; + transition: border-color 0.2s, box-shadow 0.2s; + transition-duration: 0.2s; +} + +.checkbox:hover, +.checkbox:focus-visible { + outline: none; + border-color: var(--vp-c-brand); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--vp-c-brand) 20%, transparent); } .checkbox:checked { @@ -87,4 +96,8 @@ const model = computed({ center no-repeat var(--vp-c-brand);; } +.checkbox-wrapper:hover .checkbox-label { + color: var(--vp-c-text-1); +} + diff --git a/docs/.vitepress/theme/components/base/FakeInput.vue b/docs/.vitepress/theme/components/base/FakeInput.vue index a3f0fde03..c2603f6f6 100644 --- a/docs/.vitepress/theme/components/base/FakeInput.vue +++ b/docs/.vitepress/theme/components/base/FakeInput.vue @@ -35,19 +35,37 @@ defineProps({ font-size: 14px; /* box-shadow: var(--vp-shadow-4), var(--vp-shadow-2); */ text-align: left; - border: 1px solid transparent; + border: 1px solid var(--vp-c-divider); cursor: text; display: flex; gap: 12px; transition: color 0.25s, border-color 0.25s, - background-color 0.25s; + background-color 0.25s, + box-shadow 0.25s; + transition-duration: 0.25s; } +/* A quiet border at rest; on hover the border takes the brand colour with a soft ring, + and the search icon tilts as if it starts looking */ .fake-input:hover, -.fake-input:focus { +.fake-input:focus-visible { + outline: none; + color: var(--vp-c-text-1); border-color: var(--vp-c-brand); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--vp-c-brand) 15%, transparent); +} + +.search-icon { + flex-shrink: 0; + transition: color 0.25s, transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); + transition-duration: 0.25s, 0.3s; +} + +.fake-input:hover .search-icon, +.fake-input:focus-visible .search-icon { + color: var(--vp-c-brand-1); } .shortcut { diff --git a/docs/.vitepress/theme/components/base/Input.vue b/docs/.vitepress/theme/components/base/Input.vue index 52e385ad1..1444b6b49 100644 --- a/docs/.vitepress/theme/components/base/Input.vue +++ b/docs/.vitepress/theme/components/base/Input.vue @@ -99,7 +99,7 @@ defineExpose({ .input { justify-content: flex-start; - border: 1px solid transparent; + border: 1px solid var(--vp-c-divider); border-radius: 8px; padding: 0 10px 0 12px; width: 100%; @@ -109,16 +109,21 @@ defineExpose({ transition: color 0.25s, border-color 0.25s, - background-color 0.25s; + background-color 0.25s, + box-shadow 0.25s; + transition-duration: 0.25s; } .input.has-shortcut { padding-right: calc(var(--shortcut-width, 40px) + 22px); } +/* Same as FakeInput: a quiet border at rest, a brand border with a soft ring on hover and focus */ .input:hover, .input:focus { + outline: none; border-color: var(--vp-c-brand); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--vp-c-brand) 15%, transparent); } .input:focus { background-color: var(--vp-c-bg); @@ -190,8 +195,13 @@ defineExpose({ z-index: 1; color: var(--vp-c-text-2); pointer-events: none; + transition: color 0.25s, transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); + transition-duration: 0.25s, 0.3s; } + +/* Like the FakeInput search icon, it tilts as if it starts looking */ +.input-wrapper > svg:has(+ .input.has-icon:hover), .input-wrapper > svg:has(+ .input.has-icon:focus) { - color: var(--vp-c-text-1); + color: var(--vp-c-brand-1); } diff --git a/docs/.vitepress/theme/components/base/OverviewLink.vue b/docs/.vitepress/theme/components/base/OverviewLink.vue index 9fb899e62..614118016 100644 --- a/docs/.vitepress/theme/components/base/OverviewLink.vue +++ b/docs/.vitepress/theme/components/base/OverviewLink.vue @@ -33,12 +33,45 @@ defineProps<{ padding: 11px 16px 13px; width: 100%; height: 100%; - transition: border-color 0.25s; + transition: border-color 0.25s, background-color 0.25s, transform 0.25s ease, box-shadow 0.25s; + transition-duration: 0.25s; text-decoration: none; } -.overview-link:hover { +/* On hover the card lifts with a light brand tint, like the previous / next page links, + and Lucide's arrow-right icon slides in after the title */ +.overview-link:hover, +.overview-link:focus-visible { + outline: none; border-color: var(--vp-c-brand-1); + transform: translateY(-2px); + background-color: color-mix(in srgb, var(--vp-c-brand) 6%, transparent); + box-shadow: var(--vp-shadow-2); +} + +.overview-link .title { + display: flex; + align-items: center; + gap: 6px; +} + +.overview-link .title::after { + content: ''; + flex-shrink: 0; + width: 16px; + height: 16px; + background-color: currentColor; + mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14'/%3E%3Cpath d='m12 5 7 7-7 7'/%3E%3C/svg%3E") center / contain no-repeat; + opacity: 0; + transform: translateX(-6px); + transition: opacity 0.25s, transform 0.25s ease; + transition-duration: 0.25s; +} + +.overview-link:hover .title::after, +.overview-link:focus-visible .title::after { + opacity: 1; + transform: translateX(0); } .desc { diff --git a/docs/.vitepress/theme/components/base/RangeSlider.vue b/docs/.vitepress/theme/components/base/RangeSlider.vue index 871452650..f2be66967 100644 --- a/docs/.vitepress/theme/components/base/RangeSlider.vue +++ b/docs/.vitepress/theme/components/base/RangeSlider.vue @@ -111,6 +111,36 @@ const percentage = computed(() => `${((Number(props.modelValue) - props. border-radius: 10px; box-shadow: var(--vp-shadow-2); cursor: pointer; + transition: transform 0.2s ease, box-shadow 0.2s ease; + transition-duration: 0.2s; +} + +.slider input::-moz-range-thumb { + transition: transform 0.2s ease, box-shadow 0.2s ease; + transition-duration: 0.2s; +} + +/* The thumb grows with a soft brand ring on hover and focus, and a little more while dragged */ +.slider input:hover::-webkit-slider-thumb, +.slider input:focus-visible::-webkit-slider-thumb { + transform: scale(1.1); + box-shadow: var(--vp-shadow-2), 0 0 0 5px color-mix(in srgb, var(--vp-c-brand) 25%, transparent); +} + +.slider input:active::-webkit-slider-thumb { + transform: scale(1.2); + box-shadow: var(--vp-shadow-2), 0 0 0 7px color-mix(in srgb, var(--vp-c-brand) 25%, transparent); +} + +.slider input:hover::-moz-range-thumb, +.slider input:focus-visible::-moz-range-thumb { + transform: scale(1.1); + box-shadow: var(--vp-shadow-2), 0 0 0 5px color-mix(in srgb, var(--vp-c-brand) 25%, transparent); +} + +.slider input:active::-moz-range-thumb { + transform: scale(1.2); + box-shadow: var(--vp-shadow-2), 0 0 0 7px color-mix(in srgb, var(--vp-c-brand) 25%, transparent); } .slider input::-moz-range-thumb { diff --git a/docs/.vitepress/theme/components/base/ResetButton.vue b/docs/.vitepress/theme/components/base/ResetButton.vue index 8d04d8037..f078ce45d 100644 --- a/docs/.vitepress/theme/components/base/ResetButton.vue +++ b/docs/.vitepress/theme/components/base/ResetButton.vue @@ -1,11 +1,20 @@ - + .reset-button { - transition: ease-in-out 0.1s opacity; + transition: ease-in-out 0.25s opacity; + transition-duration: 0.25s; background: none; padding: 0; } -.reset-button .lucide { - transition: ease-in-out 0.1s transform; +/* The arrow spins once in place when clicked */ +.reset-button.spinning .lucide { + animation: rotate 0.6s cubic-bezier(0.4, 0, 0.2, 1); + animation-duration: 0.6s !important; } .reset-button:hover { @@ -36,18 +48,13 @@ import Button from './Button.vue'; opacity: 0.5; } -/* a rotate css animation keyframes */ @keyframes rotate { - 0% { + from { transform: rotate(0deg); } - 100% { - transform: rotate(359deg); + to { + transform: rotate(360deg); } } - -.reset-button:active .lucide { - transform: rotate(45deg); -} diff --git a/docs/.vitepress/theme/components/base/Select.vue b/docs/.vitepress/theme/components/base/Select.vue index edece380c..c821aa2f1 100644 --- a/docs/.vitepress/theme/components/base/Select.vue +++ b/docs/.vitepress/theme/components/base/Select.vue @@ -36,6 +36,8 @@ const selected = defineModel<{ name: string; value: string; icon?: string; iconD ({ - + Style as you please @@ -116,16 +117,14 @@ const iconStyle = computed(() => ({ :style="iconStyle" /> - + diff --git a/docs/.vitepress/theme/components/icons/CategoryList.vue b/docs/.vitepress/theme/components/icons/CategoryList.vue index de450301b..b4f13dc40 100644 --- a/docs/.vitepress/theme/components/icons/CategoryList.vue +++ b/docs/.vitepress/theme/components/icons/CategoryList.vue @@ -74,7 +74,8 @@ watch(headers, () => { line-height: 24px; font-size: 14px; display: block; - transition: color 0.25s; + transition: color 0.25s, transform 0.25s ease; + transition-duration: 0.25s; padding: 4px 0; } @@ -86,6 +87,13 @@ watch(headers, () => { .sidebar-link:hover, .sidebar-link.active { color: var(--vp-c-brand); } + +/* All and Categories slide in a little on hover */ +.sidebar-link:hover, +.sidebar-link:focus-visible { + outline: none; + transform: translateX(4px); +} .content { margin-top: 12px; position: relative; @@ -101,8 +109,9 @@ watch(headers, () => { left: -1px; z-index: 0; opacity: 0; - width: 1px; + width: 2px; height: 18px; + border-radius: 1px; background-color: var(--vp-c-brand); transition: top 0.25s cubic-bezier(0, 1, 0.5, 1), background-color 0.5s, opacity 0.25s; } diff --git a/docs/.vitepress/theme/components/icons/CategoryListItem.vue b/docs/.vitepress/theme/components/icons/CategoryListItem.vue index 2ed5ed9be..96c5f2a11 100644 --- a/docs/.vitepress/theme/components/icons/CategoryListItem.vue +++ b/docs/.vitepress/theme/components/icons/CategoryListItem.vue @@ -68,22 +68,45 @@ function onClick(categoryName: string) { padding-left: 13px; } +/* Each category is a soft pill on hover: the name slides in a little and the count badge + takes the brand colour. The category in view keeps the brand colour. */ .outline-link { display: flex; - align-items: baseline; - line-height: 28px; + align-items: center; + margin: 1px -8px; + padding: 0 6px 0 8px; + line-height: 26px; color: var(--vp-c-text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - transition: color 0.5s; + border-radius: 6px; + transition: + color 0.25s, + background-color 0.25s; + transition-duration: 0.25s; font-weight: 500; } +.outline-link > span:first-child { + transition: transform 0.25s ease; + transition-duration: 0.25s; +} + .outline-link:hover, -.outline-link.active { +.outline-link:focus-visible { + outline: none; color: var(--vp-c-text-1); - transition: color 0.25s; + background-color: var(--vp-c-default-soft); +} + +.outline-link:hover > span:first-child, +.outline-link:focus-visible > span:first-child { + transform: translateX(3px); +} + +.outline-link.active { + color: var(--vp-c-brand-1); } .outline-link.inactive { @@ -96,10 +119,26 @@ function onClick(categoryName: string) { } .icon-count { - opacity: 0.5; margin-left: auto; + padding: 0 6px; + min-width: 26px; + line-height: 18px; + text-align: center; font-size: 11px; - font-weight: 400; + font-weight: 500; + color: var(--vp-c-text-3); + border-radius: 9px; + transition: + color 0.25s, + background-color 0.25s; + transition-duration: 0.25s; +} + +.outline-link:hover .icon-count, +.outline-link:focus-visible .icon-count, +.outline-link.active .icon-count { + color: var(--vp-c-brand-1); + background-color: color-mix(in srgb, var(--vp-c-brand) 14%, transparent); } .outline-link.inactive .icon-count { diff --git a/docs/.vitepress/theme/components/icons/IconDetailOverlay.vue b/docs/.vitepress/theme/components/icons/IconDetailOverlay.vue index 2b1bca928..9813ab908 100644 --- a/docs/.vitepress/theme/components/icons/IconDetailOverlay.vue +++ b/docs/.vitepress/theme/components/icons/IconDetailOverlay.vue @@ -157,6 +157,8 @@ const Expand = createLucideIcon('Expand', expand); margin: 0 auto; padding: 1rem; background-color: var(--vp-c-bg-elv); + border-top: 1px solid var(--vp-c-divider); + border-right: 1px solid var(--vp-c-divider); box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); will-change: transform; pointer-events: all; diff --git a/docs/.vitepress/theme/components/icons/SidebarIconCustomizer.vue b/docs/.vitepress/theme/components/icons/SidebarIconCustomizer.vue index 5b86325fc..a8733437e 100644 --- a/docs/.vitepress/theme/components/icons/SidebarIconCustomizer.vue +++ b/docs/.vitepress/theme/components/icons/SidebarIconCustomizer.vue @@ -7,6 +7,7 @@ import InputField from '../base/InputField.vue'; import ColorPicker from '../base/ColorPicker.vue'; import ResetButton from '../base/ResetButton.vue'; import Switch from '../base/Switch.vue'; +import Card from '../base/Card.vue'; import useTrackStyleChanges from '../../composables/useTrackStyleChanges'; import { track } from '../../utils/openpanel'; @@ -29,7 +30,7 @@ function onReset() { - @@ -95,7 +96,7 @@ function onReset() { v-model="nonScalingStroke" /> - +