{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"org-chart","type":"registry:component","title":"Org Chart","description":"A real, zoomable org chart: the one diagram Notion can't draw.","author":"Notion Arena","categories":["visuals"],"tags":["widget","pro","org-chart","team","diagram","hierarchy"],"meta":{"kind":"widget","tier":"pro","version":"1.0.0","updatedAt":"2026-09-18","notionPlan":"Free","emoji":"🧑‍🤝‍🧑","delivery":["embed","html-block","download"],"capabilities":{"interactive":true,"offlineSafe":true,"persistsLocally":false,"writesToNotion":false,"needsNetwork":false},"embedUrl":"https://notionarena.vercel.app/embed/org-chart","fileUrl":"https://notionarena.vercel.app/widgets/org-chart.html","downloadUrl":"https://notionarena.vercel.app/api/widget/org-chart","height":420,"minHeight":300,"maxHeight":800,"params":[{"key":"data","label":"People (JSON)","type":"text","default":"","help":"[{name,role,reportsTo,initials,color}]"},{"key":"accent","label":"Accent","type":"color","default":"#2383e2"},{"key":"theme","label":"Theme","type":"select","default":"auto","options":[{"value":"auto","label":"Match Notion"},{"value":"light","label":"Light"},{"value":"dark","label":"Dark"}]}]},"files":[{"path":"widgets/org-chart.html","type":"registry:file","target":"notion/widgets/org-chart.html","content":"<!doctype html>\n<html lang=\"en\" data-theme=\"auto\">\n<head>\n<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<title>Org Chart</title>\n<style>\n:root{--bg:#ffffff;--fg:#37352f;--muted:#787774;--surface:#f7f7f5;--border:#e9e9e7;--accent:#2383e2;--card:#ffffff;--line:#d9d9d7;\n--font:ui-sans-serif,-apple-system,BlinkMacSystemFont,\"Segoe UI\",Helvetica,Arial,sans-serif}\nhtml[data-theme=\"dark\"]{--bg:#191919;--fg:#e6e6e4;--muted:#9b9a97;--surface:#202020;--border:#2f2f2f;--card:#252525;--line:#3a3a3a}\n@media (prefers-color-scheme:dark){html[data-theme=\"auto\"]{--bg:#191919;--fg:#e6e6e4;--muted:#9b9a97;--surface:#202020;--border:#2f2f2f;--card:#252525;--line:#3a3a3a}}\n*{box-sizing:border-box}html,body{margin:0;height:100%}\nbody{background:var(--bg);color:var(--fg);font-family:var(--font);font-size:13px;-webkit-font-smoothing:antialiased;overflow:hidden}\n.wrap{height:100%;display:flex;flex-direction:column;overflow:hidden}\n.bar{display:flex;gap:6px;align-items:center;padding:9px 12px;border-bottom:1px solid var(--border);background:var(--surface);flex-wrap:wrap}\n.title{font-size:12.5px;font-weight:600;margin-right:auto}\nbutton{font:inherit;font-size:11px;font-weight:500;border:1px solid var(--border);background:var(--bg);color:var(--muted);border-radius:7px;padding:3px 9px;cursor:pointer}\nbutton:hover{color:var(--fg);border-color:color-mix(in oklab,var(--fg) 20%,var(--border))}\n.stage{flex:1;min-height:0;overflow:auto;position:relative;background:\n  radial-gradient(circle at 1px 1px, color-mix(in oklab,var(--fg) 7%,transparent) 1px, transparent 0);\n  background-size:20px 20px}\n.canvas{transform-origin:0 0;padding:22px;display:inline-block;min-width:100%}\n.level{display:flex;justify-content:center;gap:14px;position:relative}\n.subtree{display:flex;flex-direction:column;align-items:center}\n.node{position:relative;background:var(--card);border:1px solid var(--border);border-radius:10px;padding:7px 10px 7px 8px;\ndisplay:flex;align-items:center;gap:8px;min-width:132px;max-width:190px;box-shadow:0 1px 2px rgba(15,15,15,.05);cursor:default}\n.node.vacant{border-style:dashed;background:transparent;box-shadow:none;opacity:.75}\n.av{width:26px;height:26px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:10.5px;font-weight:700;\ncolor:#fff;flex:0 0 auto;letter-spacing:.02em;background:var(--accent)}\n.who{min-width:0}\n.who b{display:block;font-size:12px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;letter-spacing:-.005em}\n.who span{display:block;font-size:10.5px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.tw{position:absolute;right:-7px;top:-7px;width:16px;height:16px;border-radius:50%;border:1px solid var(--border);background:var(--bg);\ncolor:var(--muted);font-size:10px;line-height:14px;text-align:center;cursor:pointer;font-weight:700;user-select:none}\n.tw:hover{border-color:var(--accent);color:var(--accent)}\n.kids{display:flex;gap:14px;margin-top:20px;position:relative}\n.kids::before{content:\"\";position:absolute;top:-20px;left:50%;width:1px;height:20px;background:var(--line)}\n.kid{position:relative;padding-top:20px}\n.kid::before{content:\"\";position:absolute;top:0;left:50%;width:1px;height:20px;background:var(--line)}\n.kids>.kid:first-child::after,.kids>.kid:last-child::after{content:\"\";position:absolute;top:0;height:1px;background:var(--line)}\n.kids>.kid:first-child::after{left:50%;right:-7px}\n.kids>.kid:last-child::after{left:-7px;right:50%}\n.kids>.kid:only-child::after{display:none}\n.count{font-size:10px;color:var(--muted);background:var(--surface);border:1px solid var(--border);border-radius:99px;padding:0 6px;font-weight:600}\n.zoom{font-size:10.5px;color:var(--muted);font-variant-numeric:tabular-nums;min-width:34px;text-align:center}\n@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}\n</style>\n</head>\n<body>\n<div class=\"wrap\">\n  <div class=\"bar\">\n    <div class=\"title\" id=\"title\">Team</div>\n    <span class=\"count\" id=\"count\"></span>\n    <button id=\"out\">−</button><span class=\"zoom\" id=\"zoom\">100%</span><button id=\"in\">+</button>\n    <button id=\"expand\">Expand all</button>\n    <button id=\"collapse\">Collapse</button>\n    <button id=\"svg\">Copy SVG</button>\n  </div>\n  <div class=\"stage\" id=\"stage\"><div class=\"canvas\" id=\"canvas\"></div></div>\n</div>\n<script>\n(function(){\n  var cfgEl=document.getElementById('bk-config'),base={};\n  try{base=cfgEl?JSON.parse(cfgEl.textContent||'{}'):{};}catch(e){}\n  var q=new URLSearchParams(location.search),c={};for(var k in base)c[k]=base[k];\n  q.forEach(function(v,k){c[k]=v;});\n\n  var ACCENT=c.accent||'#2383e2';\n  if(c.theme)document.documentElement.setAttribute('data-theme',c.theme);\n  document.documentElement.style.setProperty('--accent',ACCENT);\n  if(c.title)document.getElementById('title').textContent=c.title;\n\n  var PALETTE=['#2383e2','#0f7b6c','#6940a5','#d9730d','#ad1a72','#0b6e99','#9f6b53','#dfab01'];\n  var DEMO=[\n    {name:'Amara Osei',role:'Chief Executive'},\n    {name:'Dev Raman',role:'VP Engineering',reportsTo:'Amara Osei'},\n    {name:'Lena Fischer',role:'VP Design',reportsTo:'Amara Osei'},\n    {name:'Tomás Silva',role:'Head of Revenue',reportsTo:'Amara Osei'},\n    {name:'Yuki Tanaka',role:'Platform Lead',reportsTo:'Dev Raman'},\n    {name:'Grace Adeyemi',role:'Product Eng Lead',reportsTo:'Dev Raman'},\n    {name:'Open role',role:'Staff Engineer',reportsTo:'Grace Adeyemi',vacant:true},\n    {name:'Marco Bianchi',role:'Design Systems',reportsTo:'Lena Fischer'},\n    {name:'Sara Haddad',role:'Product Design',reportsTo:'Lena Fischer'},\n    {name:'Jonas Weber',role:'Account Executive',reportsTo:'Tomás Silva'},\n    {name:'Priya Nair',role:'Solutions Engineer',reportsTo:'Tomás Silva'}\n  ];\n  var people=DEMO;\n  if(c.data){\n    try{\n      var parsed=JSON.parse(decodeURIComponent(String(c.data)));\n      if(Array.isArray(parsed)&&parsed.length)people=parsed;\n    }catch(e){}\n  }\n\n  var byName={},order=[];\n  people.forEach(function(p,i){\n    var node={id:i,name:String(p.name||'Untitled'),role:String(p.role||''),reportsTo:p.reportsTo||p.manager||null,\n      vacant:!!p.vacant,color:p.color||PALETTE[i%PALETTE.length],\n      initials:(p.initials||String(p.name||'?').split(/\\s+/).map(function(s){return s[0];}).slice(0,2).join('')).toUpperCase(),\n      kids:[],collapsed:false};\n    byName[node.name.toLowerCase()]=node;order.push(node);\n  });\n  var roots=[];\n  order.forEach(function(n){\n    var key=String(n.reportsTo||'').toLowerCase();\n    if(key&&byName[key]){byName[key].kids.push(n);}else{roots.push(n);}\n  });\n  document.getElementById('count').textContent=order.length+' people';\n\n  var canvas=document.getElementById('canvas'),stage=document.getElementById('stage');\n  var scale=1;\n  function applyZoom(){canvas.style.transform='scale('+scale+')';document.getElementById('zoom').textContent=Math.round(scale*100)+'%';}\n\n  function subtreeCount(n){return n.kids.reduce(function(a,k){return a+1+subtreeCount(k);},0);}\n  function nodeHtml(n){\n    var kids=subtreeCount(n);\n    var tw=n.kids.length?'<span class=\"tw\" data-toggle=\"'+n.id+'\">'+(n.collapsed?'+':'−')+'</span>':'';\n    return '<div class=\"node'+(n.vacant?' vacant':'')+'\" data-id=\"'+n.id+'\" title=\"'+n.name.replace(/\"/g,'')+', '+n.role.replace(/\"/g,'')+(kids?' · '+kids+' report'+(kids===1?'s':''):'')+'\">'+\n      '<span class=\"av\" style=\"background:'+(n.vacant?'transparent':n.color)+';'+(n.vacant?'border:1.5px dashed var(--line);color:var(--muted)':'')+'\">'+(n.vacant?'+':n.initials)+'</span>'+\n      '<span class=\"who\"><b>'+n.name.replace(/[<>&]/g,'')+'</b><span>'+n.role.replace(/[<>&]/g,'')+'</span></span>'+tw+'</div>';\n  }\n  function render(n){\n    if(n.collapsed||!n.kids.length)return '<div class=\"subtree\">'+nodeHtml(n)+'</div>';\n    var kids=n.kids.map(function(k){return '<div class=\"kid\">'+render(k)+'</div>';}).join('');\n    return '<div class=\"subtree\">'+nodeHtml(n)+'<div class=\"kids\">'+kids+'</div></div>';\n  }\n  function draw(){\n    canvas.innerHTML=roots.map(render).join('');\n    canvas.querySelectorAll('[data-toggle]').forEach(function(el){\n      el.addEventListener('click',function(e){\n        e.stopPropagation();\n        var n=order[parseInt(el.dataset.toggle,10)];\n        n.collapsed=!n.collapsed;draw();\n      });\n    });\n  }\n  document.getElementById('in').addEventListener('click',function(){scale=Math.min(2,scale+0.1);applyZoom();});\n  document.getElementById('out').addEventListener('click',function(){scale=Math.max(0.4,scale-0.1);applyZoom();});\n  document.getElementById('expand').addEventListener('click',function(){order.forEach(function(n){n.collapsed=false;});draw();});\n  document.getElementById('collapse').addEventListener('click',function(){order.forEach(function(n){if(n.kids.length&&n!==roots[0])n.collapsed=true;});draw();});\n  stage.addEventListener('wheel',function(e){\n    if(!(e.ctrlKey||e.metaKey))return;\n    e.preventDefault();\n    scale=Math.max(0.4,Math.min(2,scale+(e.deltaY<0?0.08:-0.08)));applyZoom();\n  },{passive:false});\n\n  document.getElementById('svg').addEventListener('click',function(e){\n    var b=e.currentTarget;\n    var w=canvas.scrollWidth,h=canvas.scrollHeight;\n    var css=Array.prototype.slice.call(document.querySelectorAll('style')).map(function(s){return s.textContent;}).join('\\n');\n    var svg='<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"'+w+'\" height=\"'+h+'\" viewBox=\"0 0 '+w+' '+h+'\">'+\n      '<foreignObject width=\"100%\" height=\"100%\"><div xmlns=\"http://www.w3.org/1999/xhtml\"><style>'+css+'</style>'+\n      canvas.innerHTML+'</div></foreignObject></svg>';\n    function done(t){b.textContent=t;setTimeout(function(){b.textContent='Copy SVG';},1400);}\n    try{navigator.clipboard.writeText(svg).then(function(){done('Copied ✓');},function(){done('See console');});}catch(err){done('See console');}\n    try{console.log(svg);}catch(err){}\n  });\n\n  draw();applyZoom();\n})();\n</script>\n</body>\n</html>\n"}]}