
3D Extruded Buildings & Urban Footprints
Real-world 3D building extrusions with dynamic sunlight shadows, terrain DEM elevations, and pitch/bearing camera controls.
Collapsible icon-rail app sidebar with brand block, two grouped nav sections, and a user dropdown footer. Pure template, no props -- edit Sidebar01.vue to change routes and NavUser.vue to wire your auth session. Pair with SidebarProvider + SidebarInset for the full admin shell.
Also available for React ->$pnpm dlx shadcn-vue@latest add https://uipkge.dev/r/vue/sidebar-01.json$npx shadcn-vue@latest add https://uipkge.dev/r/vue/sidebar-01.json$yarn dlx shadcn-vue@latest add https://uipkge.dev/r/vue/sidebar-01.json$bunx shadcn-vue@latest add https://uipkge.dev/r/vue/sidebar-01.jsonnpx shadcn-vue@latest add @uipkge/sidebar-01Installs to:app/components/blocks/sidebar-01/<script setup lang="ts">
// Edit this file to change brand, navigation, and the user dropdown.
// Every menu item is spelled out below -- copy, remove, or rename rows directly.
import { BarChart3, BookOpen, FileText, Files, History, LayoutDashboard, Table as TableIcon } from 'lucide-vue-next'
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarRail,
} from '@/components/ui/sidebar'
import NavUser from './NavUser.vue'
</script>
<template>
<Sidebar data-slot="sidebar-01" collapsible="icon">
<SidebarHeader>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton size="lg" tooltip="uipkge" class="group-data-[collapsible=icon]:!justify-center">
<div
class="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 shrink-0 items-center justify-center rounded-lg group-data-[collapsible=icon]:size-6"
>
<svg
viewBox="0 0 24 24"
class="size-4 group-data-[collapsible=icon]:size-3.5"
fill="currentColor"
aria-hidden="true"
>
<rect x="2" y="2" width="9" height="9" rx="1.5" />
<rect x="13" y="2" width="9" height="9" rx="1.5" opacity="0.55" />
<rect x="2" y="13" width="9" height="9" rx="1.5" opacity="0.55" />
<rect x="13" y="13" width="9" height="9" rx="1.5" />
</svg>
</div>
<div class="grid flex-1 text-left text-sm leading-tight group-data-[collapsible=icon]:hidden">
<span class="font-display truncate font-bold">uipkge</span>
<span class="text-muted-foreground truncate text-xs">uipkge.dev</span>
</div>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Workspace</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem>
<!-- TODO: wrap in NuxtLink / RouterLink and bind :is-active to your route -->
<SidebarMenuButton is-active tooltip="Dashboard">
<LayoutDashboard class="size-4" />
<span>Dashboard</span>
</SidebarMenuButton>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton tooltip="Data tables">
<TableIcon class="size-4" />
<span>Data tables</span>
</SidebarMenuButton>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton tooltip="Forms">
<FileText class="size-4" />
<span>Forms</span>
</SidebarMenuButton>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton tooltip="Charts">
<BarChart3 class="size-4" />
<span>Charts</span>
</SidebarMenuButton>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton tooltip="Pages">
<Files class="size-4" />
<span>Pages</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
<SidebarGroup>
<SidebarGroupLabel>Resources</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton tooltip="Docs">
<BookOpen class="size-4" />
<span>Docs</span>
</SidebarMenuButton>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton tooltip="Changelog">
<History class="size-4" />
<span>Changelog</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
<SidebarFooter>
<NavUser />
</SidebarFooter>
<SidebarRail />
</Sidebar>
</template>
<script setup lang="ts">
// Edit this file to change the user dropdown content, avatar, and logout handler.
// The user details are inlined below -- wire them to your auth session in place.
import { BadgeCheck, ChevronsUpDown, LogOut, Monitor, Moon, Palette, Settings, Sun } from 'lucide-vue-next'
import { useTheme } from '~/composables/useTheme'
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { SidebarMenu, SidebarMenuButton, SidebarMenuItem, useSidebar } from '@/components/ui/sidebar'
const { isMobile } = useSidebar()
const { theme, setTheme } = useTheme()
// Replace these with your auth session.
const user = {
name: 'Test User',
email: '[email protected]',
initials: 'TU',
}
function handleLogout() {
// TODO: call your /api/auth/logout endpoint, then redirect.
console.warn('NavUser: wire handleLogout() to your auth backend')
}
</script>
<template>
<SidebarMenu data-slot="sidebar-01">
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger as-child>
<SidebarMenuButton
size="lg"
class="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground group-data-[collapsible=icon]:!justify-center"
>
<Avatar class="h-8 w-8 shrink-0 rounded-lg group-data-[collapsible=icon]:size-6">
<AvatarFallback class="rounded-lg text-xs">{{ user.initials }}</AvatarFallback>
</Avatar>
<div class="grid flex-1 text-left text-sm leading-tight group-data-[collapsible=icon]:hidden">
<span class="truncate font-medium">{{ user.name }}</span>
<span class="truncate text-xs">{{ user.email }}</span>
</div>
<ChevronsUpDown class="ml-auto size-4 group-data-[collapsible=icon]:hidden" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent
class="w-(--reka-dropdown-menu-trigger-width) min-w-56 rounded-lg"
:side="isMobile ? 'bottom' : 'right'"
align="end"
:side-offset="4"
>
<DropdownMenuLabel class="p-0 font-normal">
<div class="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<Avatar class="h-8 w-8 rounded-lg">
<AvatarFallback class="rounded-lg">{{ user.initials }}</AvatarFallback>
</Avatar>
<div class="grid flex-1 text-left text-sm leading-tight">
<span class="truncate font-semibold">{{ user.name }}</span>
<span class="truncate text-xs">{{ user.email }}</span>
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem> <BadgeCheck class="size-4" /> Profile </DropdownMenuItem>
<DropdownMenuItem> <Settings class="size-4" /> Settings </DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<Palette class="size-4" />
Theme
<span class="text-muted-foreground ml-auto text-xs capitalize">{{ theme }}</span>
</DropdownMenuSubTrigger>
<DropdownMenuSubContent class="min-w-36">
<DropdownMenuRadioGroup
:model-value="theme"
@update:model-value="(v) => setTheme(v as 'light' | 'dark' | 'system')"
>
<DropdownMenuRadioItem value="light"> <Sun class="size-4" /> Light </DropdownMenuRadioItem>
<DropdownMenuRadioItem value="dark"> <Moon class="size-4" /> Dark </DropdownMenuRadioItem>
<DropdownMenuRadioItem value="system"> <Monitor class="size-4" /> System </DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuSubContent>
</DropdownMenuSub>
<DropdownMenuSeparator />
<DropdownMenuItem @click="handleLogout">
<LogOut class="size-4" />
Log out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
</template>
Raw manifest:https://uipkge.dev/r/vue/sidebar-01.json