442442 flex : 0 0 100% ;
443443 }
444444 }
445+ /* Accessible data-table fallback (dataviz #10) */
446+ .data-table-details {
447+ background : var (--surface );
448+ border : 1px solid var (--border );
449+ border-radius : 8px ;
450+ padding : 1rem 1.5rem ;
451+ margin-bottom : 1.5rem ;
452+ }
453+
454+ .data-table-details summary {
455+ cursor : pointer;
456+ font-weight : 600 ;
457+ color : var (--primary );
458+ }
459+
460+ .data-table-wrap {
461+ overflow-x : auto;
462+ margin-top : 1rem ;
463+ }
464+
465+ # data-table {
466+ width : 100% ;
467+ border-collapse : collapse;
468+ font-size : 0.85rem ;
469+ }
470+
471+ # data-table th ,
472+ # data-table td {
473+ text-align : left;
474+ padding : 0.4rem 0.6rem ;
475+ border-bottom : 1px solid var (--border );
476+ color : var (--text );
477+ }
478+
479+ # data-table th {
480+ position : sticky;
481+ top : 0 ;
482+ background : var (--surface );
483+ }
484+
485+ # data-table a {
486+ color : var (--primary );
487+ }
488+
445489 /* ---- Dark theme (OS default; superseded by the toggle) ---- */
446490@media (prefers-color-scheme : dark){
447491 : root : not ([data-theme = "light" ]){--primary : # 6ea6f0 ; --primary-dark : # 8fbcf5 ; --secondary : # 99a2b5 ; --background : # 101420 ; --surface : # 1a1f2e ; --border : # 2a3142 ; --text : # e8ecf4 ; --text-light : # 99a2b5 ; --success : # 34d399 ; --warning : # fbbf24 ; --error : # f87171 ; --point-stroke : # 1a1f2e ;}
470514< body >
471515 < div class ="container ">
472516 < header >
473- < h1 > 🔬 Community Embedding Space</ h1 >
517+ < h1 > Community Embedding Space</ h1 >
474518 < p class ="subtitle "> sfdp force-directed layout of 292 microbial communities based on taxonomic composition</ p >
475519 < a href ="index.html " class ="nav-link "> ← Back to Community Index</ a >
476520 </ header >
@@ -562,6 +606,27 @@ <h3>Origin</h3>
562606 </ div >
563607 </ div >
564608
609+ < details class ="data-table-details ">
610+ < summary > View data as table</ summary >
611+ < div class ="data-table-wrap ">
612+ < table id ="data-table ">
613+ < thead >
614+ < tr >
615+ < th > Community</ th >
616+ < th > ID</ th >
617+ < th > Category</ th >
618+ < th > Ecological state</ th >
619+ < th > Origin</ th >
620+ < th > Taxa</ th >
621+ < th > Interactions</ th >
622+ < th > Coverage %</ th >
623+ </ tr >
624+ </ thead >
625+ < tbody id ="data-table-body "> </ tbody >
626+ </ table >
627+ </ div >
628+ </ details >
629+
565630 < div class ="info-panel ">
566631 < h2 > How to Interpret This Visualization</ h2 >
567632 < ul >
@@ -4674,21 +4739,25 @@ <h2>How to Interpret This Visualization</h2>
46744739 const margin = { top : 20 , right : 20 , bottom : 40 , left : 40 } ;
46754740
46764741 // Color schemes for different attributes with semantic, non-clashing colors
4742+ // Categorical 8 (CVD-safe, ordered by frequency) + Other (grey).
4743+ // The 8 most frequent categories get a distinct colour; the rest
4744+ // (and OTHER) collapse to grey. Order is the colourblind-safety mechanism.
46774745 const categoryColors = {
4678- 'AMD' : '#e74c3c' , // Red - extreme acidic
4679- 'BIOMINING' : '#f39c12' , // Orange - metal extraction
4680- 'BIOREMEDIATION' : '#27ae60' , // Green - cleanup/restoration
4681- 'BIOTECHNOLOGY' : '#3498db' , // Blue - engineered systems
4682- 'CARBON_SEQUESTRATION' : '#16a085' , // Teal - carbon cycling
4683- 'DIET' : '#9b59b6' , // Purple - electron transfer
4684- 'EXTREME_ENVIRONMENT' : '#c0392b' , // Dark red - extreme
4685- 'LIGNOCELLULOSE' : '#d35400' , // Brown-orange - plant matter
4686- 'METAL_REDUCTION' : '#95a5a6' , // Gray - metal cycling
4687- 'METHANOGENESIS' : '#8e44ad' , // Dark purple - methane
4688- 'OTHER' : '#7f8c8d' , // Medium gray - uncategorized
4689- 'PHYTOPLANKTON' : '#1abc9c' , // Cyan - marine/aquatic
4690- 'RHIZOSPHERE' : '#2ecc71' , // Bright green - plant roots
4691- 'SYNTROPHY' : '#e67e22' // Orange-red - partnerships
4746+ 'RHIZOSPHERE' : '#2a78d6' , // blue
4747+ 'BIOTECHNOLOGY' : '#1baf7a' , // aqua
4748+ 'BIOREMEDIATION' : '#eda100' , // yellow
4749+ 'LIGNOCELLULOSE' : '#008300' , // green
4750+ 'SYNTROPHY' : '#4a3aa7' , // violet
4751+ 'METHANOGENESIS' : '#e34948' , // red
4752+ 'PHYTOPLANKTON' : '#e87ba4' , // magenta
4753+ 'CARBON_SEQUESTRATION' : '#eb6834' , // orange
4754+ 'OTHER' : '#898781' , // grey - collapsed
4755+ 'EXTREME_ENVIRONMENT' : '#898781' , // grey - collapsed
4756+ 'BIOMINING' : '#898781' , // grey - collapsed
4757+ 'DIET' : '#898781' , // grey - collapsed
4758+ 'AMD' : '#898781' , // grey - collapsed
4759+ 'ORAL' : '#898781' , // grey - collapsed
4760+ 'METAL_REDUCTION' : '#898781' // grey - collapsed
46924761 } ;
46934762
46944763 const stateColors = {
@@ -4704,21 +4773,24 @@ <h2>How to Interpret This Visualization</h2>
47044773 } ;
47054774
47064775 // Symbol shapes for each category (accessibility!)
4776+ // Unique shape per surviving (coloured) category; grey categories reuse
4777+ // the shapes but are distinguished from each other by shape alone.
47074778 const categorySymbols = {
4708- 'AMD' : d3 . symbolCircle , // ● Circle
4709- 'BIOMINING' : d3 . symbolSquare , // ■ Square
4710- 'BIOREMEDIATION' : d3 . symbolTriangle , // ▲ Triangle
4711- 'BIOTECHNOLOGY' : d3 . symbolDiamond , // ◆ Diamond
4712- 'CARBON_SEQUESTRATION' : d3 . symbolCross , // + Cross
4713- 'DIET' : d3 . symbolStar , // ★ Star
4714- 'EXTREME_ENVIRONMENT' : d3 . symbolWye , // Y Wye
4715- 'LIGNOCELLULOSE' : d3 . symbolCircle , // ● Circle (different color from AMD)
4716- 'METAL_REDUCTION' : d3 . symbolSquare , // ■ Square (different color from BIOMINING)
4717- 'METHANOGENESIS' : d3 . symbolTriangle , // ▲ Triangle (different color from BIOREMEDIATION)
4718- 'OTHER' : d3 . symbolDiamond , // ◆ Diamond (different color from BIOTECHNOLOGY)
4719- 'PHYTOPLANKTON' : d3 . symbolCross , // + Cross (different color from CARBON_SEQ)
4720- 'RHIZOSPHERE' : d3 . symbolStar , // ★ Star (different color from DIET)
4721- 'SYNTROPHY' : d3 . symbolWye // Y Wye (different color from EXTREME)
4779+ 'RHIZOSPHERE' : d3 . symbolCircle , // ● Circle
4780+ 'BIOTECHNOLOGY' : d3 . symbolSquare , // ■ Square
4781+ 'BIOREMEDIATION' : d3 . symbolTriangle , // ▲ Triangle
4782+ 'LIGNOCELLULOSE' : d3 . symbolDiamond , // ◆ Diamond
4783+ 'SYNTROPHY' : d3 . symbolCross , // + Cross
4784+ 'METHANOGENESIS' : d3 . symbolStar , // ★ Star
4785+ 'PHYTOPLANKTON' : d3 . symbolWye , // Y Wye
4786+ 'CARBON_SEQUESTRATION' : d3 . symbolCircle , // ● Circle (distinct colour)
4787+ 'OTHER' : d3 . symbolDiamond , // ◆ grey
4788+ 'EXTREME_ENVIRONMENT' : d3 . symbolWye , // Y grey
4789+ 'BIOMINING' : d3 . symbolSquare , // ■ grey
4790+ 'DIET' : d3 . symbolStar , // ★ grey
4791+ 'AMD' : d3 . symbolCircle , // ● grey
4792+ 'ORAL' : d3 . symbolTriangle , // ▲ grey
4793+ 'METAL_REDUCTION' : d3 . symbolCross // + grey
47224794 } ;
47234795
47244796 const colorSchemes = {
@@ -5293,6 +5365,29 @@ <h2>How to Interpret This Visualization</h2>
52935365 // Initial render
52945366 updateVisualization ( ) ;
52955367
5368+ // Populate the accessible data-table fallback (dataviz #10) from the
5369+ // same data the plot renders.
5370+ ( function buildDataTable ( ) {
5371+ const tbody = document . getElementById ( 'data-table-body' ) ;
5372+ if ( ! tbody ) return ;
5373+ const esc = s => String ( s == null ? '' : s )
5374+ . replace ( / & / g, '&' ) . replace ( / < / g, '<' ) . replace ( / > / g, '>' ) ;
5375+ const rows = communityData . slice ( ) . sort ( ( a , b ) => a . name . localeCompare ( b . name ) ) ;
5376+ tbody . innerHTML = rows . map ( d =>
5377+ '<tr>' +
5378+ '<td><a href="' + esc ( d . url ) + '">' + esc ( d . name ) + '</a></td>' +
5379+ '<td>' + esc ( d . id ) + '</td>' +
5380+ '<td>' + esc ( d . category ) + '</td>' +
5381+ '<td>' + esc ( d . ecological_state ) + '</td>' +
5382+ '<td>' + esc ( d . origin ) + '</td>' +
5383+ '<td>' + esc ( d . num_taxa ) + '</td>' +
5384+ '<td>' + esc ( d . num_interactions ) + '</td>' +
5385+ '<td>' + ( typeof d . coverage_pct === 'number'
5386+ ? d . coverage_pct . toFixed ( 1 ) : esc ( d . coverage_pct ) ) + '</td>' +
5387+ '</tr>'
5388+ ) . join ( '' ) ;
5389+ } ) ( ) ;
5390+
52965391 // Add axes
52975392 const xAxis = d3 . axisBottom ( xScale ) . ticks ( 5 ) ;
52985393 const yAxis = d3 . axisLeft ( yScale ) . ticks ( 5 ) ;
@@ -5305,7 +5400,7 @@ <h2>How to Interpret This Visualization</h2>
53055400 . attr ( 'y' , 35 )
53065401 . attr ( 'fill' , 'currentColor' )
53075402 . attr ( 'text-anchor' , 'middle' )
5308- . text ( 'UMAP Dimension 1' ) ;
5403+ . text ( 'Layout Dimension 1' ) ;
53095404
53105405 svg . append ( 'g' )
53115406 . attr ( 'transform' , `translate(${ margin . left } ,0)` )
@@ -5316,7 +5411,7 @@ <h2>How to Interpret This Visualization</h2>
53165411 . attr ( 'y' , - 30 )
53175412 . attr ( 'fill' , 'currentColor' )
53185413 . attr ( 'text-anchor' , 'middle' )
5319- . text ( 'UMAP Dimension 2' ) ;
5414+ . text ( 'Layout Dimension 2' ) ;
53205415 </ script >
53215416 < script src ="theme-toggle.js "> </ script >
53225417</ body >
0 commit comments