まとめてコピー
コード・使い方を見る HTML CSS JS
< div class = "tc-tabs" >
< p class = "tc-heading" >診療のご案内</ p >
< div class = "tc-tablist" role = "tablist" aria-label = "診療内容の切り替え" >
< button type = "button" class = "tc-tab" role = "tab" aria-selected = "true" >内科</ button >
< button type = "button" class = "tc-tab" role = "tab" aria-selected = "false" tabindex = "-1" >小児科</ button >
< button type = "button" class = "tc-tab" role = "tab" aria-selected = "false" tabindex = "-1" >生活習慣病</ button >
</ div >
< div class = "tc-panel" role = "tabpanel" >
< h3 class = "tc-panel-title" >内科</ h3 >
< p class = "tc-panel-text" >発熱・咳・腹痛などの体調不良から、健康診断の事後相談まで幅広く対応します。まずはお気軽にご相談ください。</ p >
</ div >
< div class = "tc-panel" role = "tabpanel" hidden >
< h3 class = "tc-panel-title" >小児科</ h3 >
< p class = "tc-panel-text" >お子さまの発熱・予防接種・乳幼児健診に対応しています。保護者の方にも分かりやすい説明を心がけています。</ p >
</ div >
< div class = "tc-panel" role = "tabpanel" hidden >
< h3 class = "tc-panel-title" >生活習慣病</ h3 >
< p class = "tc-panel-text" >高血圧・糖尿病・脂質異常症などの継続的な管理を、検査データをもとに丁寧にサポートします。</ p >
</ div >
</ div > .tc-tabs {
--tc-accent : #2f8f9d ; /* タブ・見出しのアクセントカラー(医院のテーマ色に変更してください) */
max-width : 720 px ;
margin : 0 auto ;
font-family : "Noto Sans JP" , "Hiragino Kaku Gothic ProN" , sans-serif ;
}
.tc-heading {
margin : 0 0 1 rem ;
font-size : 1.15 rem ;
font-weight : 700 ;
color : #1f2937 ;
text-align : center ;
letter-spacing : .05 em ;
}
.tc-tablist {
display : flex ;
gap : .5 rem ;
border-bottom : 2 px solid #e5e7eb ;
}
.tc-tab {
flex : 1 ;
appearance : none ;
border : none ;
background : transparent ;
padding : .8 em .5 em ;
font-size : .92 rem ;
font-weight : 600 ;
font-family : inherit ;
color : #6b7280 ;
cursor : pointer ;
position : relative ;
transition : color .15 s ;
}
.tc-tab::after {
content : "" ;
position : absolute ;
left : 0 ;
right : 0 ;
bottom : -2 px ;
height : 2 px ;
background : var ( --tc-accent );
transform : scaleX ( 0 );
transition : transform .2 s ease ;
}
.tc-tab:hover {
color : var ( --tc-accent );
}
.tc-tab:focus-visible {
outline : 2 px solid var ( --tc-accent );
outline-offset : 2 px ;
}
.tc-tab [ aria-selected = "true" ] {
color : var ( --tc-accent );
}
.tc-tab [ aria-selected = "true" ] ::after {
transform : scaleX ( 1 );
}
.tc-panel {
padding : 1.6 rem .2 rem ;
animation : tc-fade .25 s ease ;
}
@keyframes tc-fade {
from { opacity : 0 ; transform : translateY ( 4 px ); }
to { opacity : 1 ; transform : translateY ( 0 ); }
}
.tc-panel-title {
margin : 0 0 .6 rem ;
font-size : 1 rem ;
font-weight : 700 ;
color : var ( --tc-accent );
}
.tc-panel-text {
margin : 0 ;
font-size : .9 rem ;
line-height : 1.8 ;
color : #374151 ;
}
@media ( max-width : 480 px ) {
.tc-tab {
font-size : .8 rem ;
padding : .7 em .3 em ;
}
.tc-panel-text {
font-size : .85 rem ;
}
}
@media (prefers-reduced-motion: reduce) {
.tc-panel {
animation : none ;
}
.tc-tab::after {
transition : none ;
}
} ( function () {
var instanceCount = 0 ;
function TabContent ( root ) {
instanceCount += 1 ;
this .uid = 'tc-auto-' + instanceCount;
this .root = root;
this .tablist = root. querySelector ( '.tc-tablist' );
this .tabs = Array . prototype .slice. call (root. querySelectorAll ( '.tc-tab' ));
this .panels = Array . prototype .slice. call (root. querySelectorAll ( '.tc-panel' ));
if ( ! this .tablist || this .tabs. length === 0 || this .tabs. length !== this .panels. length ) return ;
this . setupIds ();
this . bindEvents ();
}
// HTML側に id を書かせないため、JS側で一意な id を生成して
// role="tab" と role="tabpanel" を aria-controls / aria-labelledby で結びつける
TabContent . prototype . setupIds = function () {
var self = this ;
this .tabs. forEach ( function ( tab , i ) {
var tabId = self.uid + '-tab-' + i;
var panelId = self.uid + '-panel-' + i;
tab.id = tabId;
tab. setAttribute ( 'aria-controls' , panelId);
self.panels[i].id = panelId;
self.panels[i]. setAttribute ( 'aria-labelledby' , tabId);
});
};
TabContent . prototype . select = function ( index , options ) {
var focusTab = options && options.focusTab;
var count = this .tabs. length ;
var next = (index + count) % count;
this .tabs. forEach ( function ( tab , i ) {
var isActive = i === next;
tab. setAttribute ( 'aria-selected' , String (isActive));
tab. setAttribute ( 'tabindex' , isActive ? '0' : '-1' );
});
this .panels. forEach ( function ( panel , i ) {
panel.hidden = i !== next;
});
if (focusTab) {
this .tabs[next]. focus ();
}
};
TabContent . prototype . bindEvents = function () {
var self = this ;
this .tabs. forEach ( function ( tab , i ) {
tab. addEventListener ( 'click' , function () {
self. select (i);
});
});
this .tablist. addEventListener ( 'keydown' , function ( e ) {
var current = self.tabs. findIndex ( function ( tab ) {
return tab. getAttribute ( 'aria-selected' ) === 'true' ;
});
if (current === - 1 ) current = 0 ;
if (e.key === 'ArrowRight' ) {
e. preventDefault ();
self. select (current + 1 , { focusTab: true });
} else if (e.key === 'ArrowLeft' ) {
e. preventDefault ();
self. select (current - 1 , { focusTab: true });
} else if (e.key === 'Home' ) {
e. preventDefault ();
self. select ( 0 , { focusTab: true });
} else if (e.key === 'End' ) {
e. preventDefault ();
self. select (self.tabs. length - 1 , { focusTab: true });
}
});
};
document. querySelectorAll ( '.tc-tabs' ). forEach ( function ( root ) {
new TabContent (root);
});
})(); 使い方メモ タブの見出しは <button class="tc-tab">…</button> の文字を書き換えてください。 本文は対応する <div class="tc-panel"> の中の <h3 class="tc-panel-title"> と <p class="tc-panel-text"> を書き換えてください。 タブと中身は上から順番に対応しています(1番目のタブ→1番目のパネル)。増減する場合はタブとパネルを同じ数だけ追加・削除してください。 キーボードの ←→ キーでもタブを移動できます。 色は CSS 先頭の --tc-accent の色コードを医院のテーマ色に変更します。