WordPressテーマ「SWELL」でブログを運営していると、タグクラウドをもう少し見た目よく表示したいと思うことがあります。通常のタグクラウドは、タグ名が横並びで表示されるシンプルなものです。もちろん、それだけでも十分使いやすいです。
しかし、サイトの雰囲気や記事のテーマによっては、タグを単純に並べるだけではなく、もう少しデザイン性を持たせたい場合もあります。
そこで今回は、SWELLで使える自作カスタマイズとして、指定した文字の形にタグを配置する「文字型タグクラウド」を作成しました。
本記事では、図形・記号・文字でタグクラウドをどのように表現できるのかを紹介します。タグがタイポグラフィー風に表示されるので、通常のタグクラウドとは違った見た目を楽しめます。
文字の形にタグを配置するタグクラウド
通常のタグクラウドは、タグ名が一覧のように並ぶシンプルな表示です。しかし、サイトの雰囲気や記事内容によっては、もう少し視覚的に見せたい場合もあります。そこで今回は、タグを単純に並べるのではなく、指定した文字の形に沿って配置する「図形・記号・文字型タグクラウド」を作成しました。
たとえば、「や」「り」「た」「い」「こ」「と」など、ショートコードで指定した文字の形をもとに、その中へタグを配置できます。タグは横表示だけでなく、縦表示にも対応しています。ただし、小さいタグだけ横表示に設定です。
タグ一つ一つの読みやすさも調整できます。タグ同士の左右間隔、上下間隔、タグ内の字間、分離した文字パーツ同士の間隔も指定できるようにしました。そのため、「こ」のように上の点と下の本体が分かれている文字でも、タグがくっつきすぎないように調整できます。
文字によって向き不向きがある
文字型タグクラウドは、どんな文字でも同じようにきれいに表示できるわけではありません。向いているのは、形がはっきりしている文字です。
次のような文字は比較的使いやすいです。
「つ、て、と、一、十、人、大、口」
一方で、画数が多すぎる漢字や、線が細かく入り組んだ文字は、タグが入りにくくなります。ひらがなは曲線が多いため、文字によっては調整が必要です。
「う」や「こ」のようにパーツが分かれている文字は、分離部分の間隔を調整すると見やすくなります。今回の自作カスタマイズでは、「component_gap」を使って上下の間隔を調整できるようにしています。
他にも、タグ数が少なければ、文字を作ることができません。ある程度のタグ数と調整が必要です。
図形・記号・文字型タグクラウドの導入設定
PHPコード・CSSコード・ショートコードを追加し設定を行います。
文字型タグクラウドは、SWELLの標準機能ではありません。PHP・CSS・JavaScriptを使った自作カスタマイズです。使用する場合は、子テーマなどを使い、事前にバックアップを取ったうえで試す方法が安全です。
また、ページの表示速度への影響を抑えるには、最初に表示されるファーストビューを避け、記事下やサイドバーの中段、フッター付近などに設置するとよいでしょう。ただし、初期表示の負担を軽減するには、遅延読み込みなどの設定も重要です。
使用中の子テーマの functions.php を開きます。すでに書かれているコードは削除せず、追記できる位置に、以下のコード全体を追加します。

PHPコード全文
// ---------------------------------------------------
// 自由文字タグクラウド 軽量版 fill
// [custom_tag_cloud_text_mask_light_fill]
// 外周回転なし / 横表示 / 縦表示 / smallestだけ横表示
// letter_spacing / gap_x / gap_y / component_gap 対応
// スマホ時:タグ文字だけ mobile_font_scale で縮小
// ---------------------------------------------------
function custom_tag_cloud_text_mask_light_fill_shortcode($atts) {
$atts = shortcode_atts( array(
'text' => '中',
'smallest' => 8,
'largest' => 40,
'unit' => 'px',
'number' => 0,
'width' => 600,
'height' => 600,
'font_size' => 520,
'stroke_width' => 0,
'font_family' => 'serif',
'vertical' => 0,
'smallest_horizontal' => 1,
'step' => 5,
'letter_spacing' => '0',
'gap_x' => 3,
'gap_y' => 1,
'component_gap' => 10,
// スマホ時のタグ文字だけの縮小率
// 0.333 = 約3分の1
// 0.4 = 少し大きめ
// 0.5 = 半分
'mobile_font_scale' => 0.333,
), $atts, 'custom_tag_cloud_text_mask_light_fill' );
$unit = in_array( $atts['unit'], array( 'px', 'em', 'rem' ), true ) ? $atts['unit'] : 'px';
$width = max( 1, absint( $atts['width'] ) );
$height = max( 1, absint( $atts['height'] ) );
$font_size = max( 1, floatval( $atts['font_size'] ) );
$stroke_width = max( 0, floatval( $atts['stroke_width'] ) );
$vertical = absint( $atts['vertical'] );
$smallest_horizontal = absint( $atts['smallest_horizontal'] );
$step = max( 2, absint( $atts['step'] ) );
$gap_x = max( 0, absint( $atts['gap_x'] ) );
$gap_y = max( 0, absint( $atts['gap_y'] ) );
$component_gap = max( 0, absint( $atts['component_gap'] ) );
// 0.1〜1の範囲に制限
$mobile_font_scale = max( 0.1, min( 1, floatval( $atts['mobile_font_scale'] ) ) );
$text = sanitize_text_field( $atts['text'] );
$font_family = sanitize_text_field( $atts['font_family'] );
// 字間指定。0 / normal / px / em / rem / % のみ許可
$letter_spacing = sanitize_text_field( $atts['letter_spacing'] );
if (
$letter_spacing !== '0' &&
$letter_spacing !== 'normal' &&
! preg_match( '/^-?(?:\d+|\d*.\d+)(px|em|rem|%)$/', $letter_spacing )
) {
$letter_spacing = '0';
}
$tags = get_tags( array(
'orderby' => 'count',
'order' => 'DESC',
'number' => absint( $atts['number'] ),
'hide_empty' => true,
) );
if ( empty( $tags ) ) return '';
$counts = wp_list_pluck( $tags, 'count' );
$min = min( $counts );
$max = max( $counts );
$spread = max( 1, $max - $min );
$font_spread = max( 1, (float) $atts['largest'] - (float) $atts['smallest'] );
$font_step = $font_spread / $spread;
$html = '<div class="custom-tag-cloud-text-mask-light-fill"';
$html .= ' style="--tag-cloud-text-mask-light-width:' . esc_attr( $width ) . 'px; --tag-cloud-text-mask-light-aspect:' . esc_attr( $width ) . ' / ' . esc_attr( $height ) . '; --tag-cloud-text-mask-light-letter-spacing:' . esc_attr( $letter_spacing ) . ';"';
$html .= ' data-mask-light-text="' . esc_attr( $text ) . '"';
$html .= ' data-mask-light-width="' . esc_attr( $width ) . '"';
$html .= ' data-mask-light-height="' . esc_attr( $height ) . '"';
$html .= ' data-mask-light-font-size="' . esc_attr( $font_size ) . '"';
$html .= ' data-mask-light-stroke-width="' . esc_attr( $stroke_width ) . '"';
$html .= ' data-mask-light-font-family="' . esc_attr( $font_family ) . '"';
$html .= ' data-mask-light-vertical="' . esc_attr( $vertical ) . '"';
$html .= ' data-mask-light-smallest-horizontal="' . esc_attr( $smallest_horizontal ) . '"';
$html .= ' data-mask-light-step="' . esc_attr( $step ) . '"';
$html .= ' data-mask-light-gap-x="' . esc_attr( $gap_x ) . '"';
$html .= ' data-mask-light-gap-y="' . esc_attr( $gap_y ) . '"';
$html .= ' data-mask-light-component-gap="' . esc_attr( $component_gap ) . '"';
$html .= ' data-mask-light-mobile-font-scale="' . esc_attr( $mobile_font_scale ) . '"';
$html .= '>';
foreach ( $tags as $tag ) {
$size = (float) $atts['smallest'] + ( ( $tag->count - $min ) * $font_step );
$size = round( $size, 2 );
$class = 'custom-tag-' . sanitize_html_class( $tag->slug );
// smallestに該当するタグだけ横表示用クラスを付ける
if ( $smallest_horizontal && abs( $size - (float) $atts['smallest'] ) < 0.01 ) {
$class .= ' is-smallest-horizontal';
}
$html .= '<a href="' . esc_url( get_tag_link( $tag->term_id ) ) . '" class="' . esc_attr( $class ) . '" data-mask-light-base-font-size="' . esc_attr( $size ) . '" data-mask-light-font-unit="' . esc_attr( $unit ) . '" style="font-size:' . esc_attr( $size ) . esc_attr( $unit ) . ';" title="' . esc_attr( $tag->count ) . ' 件の投稿">';
$html .= esc_html( $tag->name );
$html .= '</a>';
}
$html .= '</div>';
return $html;
}
add_shortcode('custom_tag_cloud_text_mask_light_fill', 'custom_tag_cloud_text_mask_light_fill_shortcode');
// ---------------------------------------------------
// 自由文字タグクラウド 軽量版 random tight
// [custom_tag_cloud_text_mask_light_random_tight]
// 外周回転なし / 横表示 / 縦表示 / smallestだけ横表示
// letter_spacing / gap_x / gap_y / component_gap 対応
// スマホ時:タグ文字だけ mobile_font_scale で縮小
// ---------------------------------------------------
function custom_tag_cloud_text_mask_light_random_tight_shortcode($atts) {
$atts = shortcode_atts( array(
'text' => '中',
'smallest' => 8,
'largest' => 40,
'unit' => 'px',
'number' => 0,
'width' => 600,
'height' => 600,
'font_size' => 520,
'stroke_width' => 0,
'font_family' => 'serif',
'vertical' => 0,
'smallest_horizontal' => 1,
'step' => 4,
'letter_spacing' => '0',
'gap_x' => 3,
'gap_y' => 0,
'component_gap' => 10,
// スマホ時のタグ文字だけの縮小率
// 0.333 = 約3分の1
// 0.4 = 少し大きめ
// 0.5 = 半分
'mobile_font_scale' => 0.333,
), $atts, 'custom_tag_cloud_text_mask_light_random_tight' );
$unit = in_array( $atts['unit'], array( 'px', 'em', 'rem' ), true ) ? $atts['unit'] : 'px';
$width = max( 1, absint( $atts['width'] ) );
$height = max( 1, absint( $atts['height'] ) );
$font_size = max( 1, floatval( $atts['font_size'] ) );
$stroke_width = max( 0, floatval( $atts['stroke_width'] ) );
$vertical = absint( $atts['vertical'] );
$smallest_horizontal = absint( $atts['smallest_horizontal'] );
$step = max( 2, absint( $atts['step'] ) );
$gap_x = max( 0, absint( $atts['gap_x'] ) );
$gap_y = max( 0, absint( $atts['gap_y'] ) );
$component_gap = max( 0, absint( $atts['component_gap'] ) );
// 0.1〜1の範囲に制限
$mobile_font_scale = max( 0.1, min( 1, floatval( $atts['mobile_font_scale'] ) ) );
$text = sanitize_text_field( $atts['text'] );
$font_family = sanitize_text_field( $atts['font_family'] );
// 字間指定。0 / normal / px / em / rem / % のみ許可
$letter_spacing = sanitize_text_field( $atts['letter_spacing'] );
if (
$letter_spacing !== '0' &&
$letter_spacing !== 'normal' &&
! preg_match( '/^-?(?:\d+|\d*.\d+)(px|em|rem|%)$/', $letter_spacing )
) {
$letter_spacing = '0';
}
$tags = get_tags( array(
'orderby' => 'count',
'order' => 'DESC',
'number' => absint( $atts['number'] ),
'hide_empty' => true,
) );
if ( empty( $tags ) ) return '';
$counts = wp_list_pluck( $tags, 'count' );
$min = min( $counts );
$max = max( $counts );
$spread = max( 1, $max - $min );
$font_spread = max( 1, (float) $atts['largest'] - (float) $atts['smallest'] );
$font_step = $font_spread / $spread;
$html = '<div class="custom-tag-cloud-text-mask-light-random-tight"';
$html .= ' style="--tag-cloud-text-mask-light-width:' . esc_attr( $width ) . 'px; --tag-cloud-text-mask-light-aspect:' . esc_attr( $width ) . ' / ' . esc_attr( $height ) . '; --tag-cloud-text-mask-light-letter-spacing:' . esc_attr( $letter_spacing ) . ';"';
$html .= ' data-mask-light-text="' . esc_attr( $text ) . '"';
$html .= ' data-mask-light-width="' . esc_attr( $width ) . '"';
$html .= ' data-mask-light-height="' . esc_attr( $height ) . '"';
$html .= ' data-mask-light-font-size="' . esc_attr( $font_size ) . '"';
$html .= ' data-mask-light-stroke-width="' . esc_attr( $stroke_width ) . '"';
$html .= ' data-mask-light-font-family="' . esc_attr( $font_family ) . '"';
$html .= ' data-mask-light-vertical="' . esc_attr( $vertical ) . '"';
$html .= ' data-mask-light-smallest-horizontal="' . esc_attr( $smallest_horizontal ) . '"';
$html .= ' data-mask-light-step="' . esc_attr( $step ) . '"';
$html .= ' data-mask-light-gap-x="' . esc_attr( $gap_x ) . '"';
$html .= ' data-mask-light-gap-y="' . esc_attr( $gap_y ) . '"';
$html .= ' data-mask-light-component-gap="' . esc_attr( $component_gap ) . '"';
$html .= ' data-mask-light-mobile-font-scale="' . esc_attr( $mobile_font_scale ) . '"';
$html .= '>';
foreach ( $tags as $tag ) {
$size = (float) $atts['smallest'] + ( ( $tag->count - $min ) * $font_step );
$size = round( $size, 2 );
$class = 'custom-tag-' . sanitize_html_class( $tag->slug );
// smallestに該当するタグだけ横表示用クラスを付ける
if ( $smallest_horizontal && abs( $size - (float) $atts['smallest'] ) < 0.01 ) {
$class .= ' is-smallest-horizontal';
}
$html .= '<a href="' . esc_url( get_tag_link( $tag->term_id ) ) . '" class="' . esc_attr( $class ) . '" data-mask-light-base-font-size="' . esc_attr( $size ) . '" data-mask-light-font-unit="' . esc_attr( $unit ) . '" style="font-size:' . esc_attr( $size ) . esc_attr( $unit ) . ';" title="' . esc_attr( $tag->count ) . ' 件の投稿">';
$html .= esc_html( $tag->name );
$html .= '</a>';
}
$html .= '</div>';
return $html;
}
add_shortcode('custom_tag_cloud_text_mask_light_random_tight', 'custom_tag_cloud_text_mask_light_random_tight_shortcode');
// ---------------------------------------------------
// 自由文字タグクラウド 軽量版 JavaScript
// fill版 / random tight版 共通
// 分離文字対応:同じパーツ内に入るタグだけ配置
// スマホ時:タグ文字だけ mobile_font_scale で縮小
// ---------------------------------------------------
function custom_tag_cloud_text_mask_light_scripts() {
?>
<script>
document.addEventListener('DOMContentLoaded', function () {
function shuffleArrayMaskLight(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
const temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
function getMaskLightMobileFontScale(cloud) {
const raw = parseFloat(cloud.dataset.maskLightMobileFontScale || '0.333');
if (!window.matchMedia('(max-width: 600px)').matches) {
return 1;
}
if (!isFinite(raw) || raw <= 0) {
return 1;
}
return raw;
}
function applyMaskLightTagFontSize(cloud, tags) {
const fontScale = getMaskLightMobileFontScale(cloud);
tags.forEach(function(tag) {
const baseSize = parseFloat(tag.dataset.maskLightBaseFontSize || '12');
const unit = tag.dataset.maskLightFontUnit || 'px';
if (!isFinite(baseSize) || baseSize <= 0) {
return;
}
tag.style.fontSize = (baseSize * fontScale) + unit;
});
}
function createMaskLightCanvas(cloud) {
const boxW = Math.round(cloud.clientWidth);
const boxH = Math.round(cloud.clientHeight);
const baseW = parseFloat(cloud.dataset.maskLightWidth || 600);
const baseH = parseFloat(cloud.dataset.maskLightHeight || 600);
const fontSizeBase = parseFloat(cloud.dataset.maskLightFontSize || 520);
const strokeWidthBase = parseFloat(cloud.dataset.maskLightStrokeWidth || 0);
const fontFamily = cloud.dataset.maskLightFontFamily || 'serif';
const text = cloud.dataset.maskLightText || '中';
const scaleX = boxW / baseW;
const scaleY = boxH / baseH;
const scale = Math.min(scaleX, scaleY);
const fontSize = fontSizeBase * scale;
const strokeWidth = strokeWidthBase * scale;
const canvas = document.createElement('canvas');
canvas.width = boxW;
canvas.height = boxH;
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, boxW, boxH);
ctx.fillStyle = '#000';
ctx.strokeStyle = '#000';
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.font = '700 ' + fontSize + 'px ' + fontFamily;
const cx = boxW / 2;
const cy = boxH / 2;
if (strokeWidth > 0) {
ctx.lineWidth = strokeWidth * 2;
ctx.strokeText(text, cx, cy);
}
ctx.fillText(text, cx, cy);
const img = ctx.getImageData(0, 0, boxW, boxH);
const data = img.data;
function alphaAt(x, y) {
if (x < 0 || y < 0 || x >= boxW || y >= boxH) return 0;
const ix = (Math.floor(y) * boxW + Math.floor(x)) * 4 + 3;
return data[ix];
}
function isInside(x, y) {
return alphaAt(x, y) > 20;
}
// --------------------------------------------
// 文字の黒い部分をパーツごとに分ける
// 例:「う」なら上の点と下の本体を別パーツとして扱う
// --------------------------------------------
const labels = new Int32Array(boxW * boxH);
const componentSizes = [0];
let componentId = 0;
function indexOf(x, y) {
return y * boxW + x;
}
for (let y = 0; y < boxH; y++) {
for (let x = 0; x < boxW; x++) {
const idx = indexOf(x, y);
if (labels[idx] !== 0) continue;
if (alphaAt(x, y) <= 20) continue;
componentId++;
let size = 0;
const stack = [idx];
labels[idx] = componentId;
while (stack.length) {
const current = stack.pop();
size++;
const cx2 = current % boxW;
const cy2 = Math.floor(current / boxW);
const neighbors = [
[cx2 + 1, cy2],
[cx2 - 1, cy2],
[cx2, cy2 + 1],
[cx2, cy2 - 1]
];
for (let i = 0; i < neighbors.length; i++) {
const nx = neighbors[i][0];
const ny = neighbors[i][1];
if (nx < 0 || ny < 0 || nx >= boxW || ny >= boxH) continue;
const nidx = indexOf(nx, ny);
if (labels[nidx] !== 0) continue;
if (alphaAt(nx, ny) <= 20) continue;
labels[nidx] = componentId;
stack.push(nidx);
}
}
componentSizes[componentId] = size;
}
}
function componentAt(x, y) {
if (x < 0 || y < 0 || x >= boxW || y >= boxH) return 0;
return labels[indexOf(Math.floor(x), Math.floor(y))];
}
return {
width: boxW,
height: boxH,
isInside: isInside,
componentAt: componentAt,
componentSizes: componentSizes
};
}
// タグ全体が「同じ文字パーツ内」に入るか判定
function rectInsideMaskLight(mask, rect) {
const points = [
{ x: rect.left, y: rect.top },
{ x: rect.right, y: rect.top },
{ x: rect.left, y: rect.bottom },
{ x: rect.right, y: rect.bottom },
// 辺の中央
{ x: (rect.left + rect.right) / 2, y: rect.top },
{ x: rect.right, y: (rect.top + rect.bottom) / 2 },
{ x: (rect.left + rect.right) / 2, y: rect.bottom },
{ x: rect.left, y: (rect.top + rect.bottom) / 2 },
// 中心
{ x: (rect.left + rect.right) / 2, y: (rect.top + rect.bottom) / 2 }
];
let componentId = 0;
for (let i = 0; i < points.length; i++) {
const p = points[i];
if (!mask.isInside(p.x, p.y)) {
return 0;
}
const cid = mask.componentAt(p.x, p.y);
if (cid === 0) {
return 0;
}
if (componentId === 0) {
componentId = cid;
} else if (componentId !== cid) {
// 別パーツをまたぐタグは配置しない
return 0;
}
}
return componentId;
}
// 軽量版の外側判定
function outerScoreMaskLight(rect, mask) {
const cx = (rect.left + rect.right) / 2;
const cy = (rect.top + rect.bottom) / 2;
const distLeft = cx;
const distRight = mask.width - cx;
const distTop = cy;
const distBottom = mask.height - cy;
return Math.min(distLeft, distRight, distTop, distBottom);
}
function overlapsMaskLight(rect, placed, gapX, gapY, componentGap, componentId) {
return placed.some(function (r) {
const useGapX = (r.componentId !== componentId) ? Math.max(gapX, componentGap) : gapX;
const useGapY = (r.componentId !== componentId) ? Math.max(gapY, componentGap) : gapY;
return !(
rect.right + useGapX < r.left ||
rect.left > r.right + useGapX ||
rect.bottom + useGapY < r.top ||
rect.top > r.bottom + useGapY
);
});
}
function positionMaskLightCloud(cloud, randomMode) {
const tags = Array.from(cloud.querySelectorAll('a'));
// ここで先にスマホ用のタグ文字サイズを反映する
// その後に offsetWidth / offsetHeight を測るので、配置計算も縮小後サイズで行われる
applyMaskLightTagFontSize(cloud, tags);
const mask = createMaskLightCanvas(cloud);
const step = parseInt(cloud.dataset.maskLightStep || (randomMode ? '4' : '5'), 10);
const gapX = parseInt(cloud.dataset.maskLightGapX || '3', 10);
const gapY = parseInt(cloud.dataset.maskLightGapY || (randomMode ? '0' : '1'), 10);
const componentGap = parseInt(cloud.dataset.maskLightComponentGap || '10', 10);
const edgePadding = 4;
const bandSize = 10;
const placed = [];
tags.forEach(function(tag) {
tag.style.display = 'inline-block';
tag.style.position = 'absolute';
tag.style.left = '0px';
tag.style.top = '0px';
tag.style.visibility = 'hidden';
tag.style.opacity = '0';
tag.style.transform = 'none';
});
// PHP側で投稿数が多い順=大きい順に出力済み
tags.forEach(function(tag) {
const tagW = tag.offsetWidth;
const tagH = tag.offsetHeight;
let placedThisTag = false;
let candidates = [];
for (let y = edgePadding; y <= mask.height - tagH - edgePadding; y += step) {
for (let x = edgePadding; x <= mask.width - tagW - edgePadding; x += step) {
const rect = {
left: x,
top: y,
right: x + tagW,
bottom: y + tagH
};
const componentId = rectInsideMaskLight(mask, rect);
if (!componentId) {
continue;
}
const score = outerScoreMaskLight(rect, mask);
candidates.push({
x: x,
y: y,
score: score,
band: Math.floor(score / bandSize),
rect: rect,
componentId: componentId
});
}
}
if (randomMode) {
const groups = {};
candidates.forEach(function(item) {
if (!groups[item.band]) {
groups[item.band] = [];
}
groups[item.band].push(item);
});
candidates = [];
Object.keys(groups).sort(function(a, b) {
return Number(a) - Number(b);
}).forEach(function(key) {
candidates = candidates.concat(shuffleArrayMaskLight(groups[key]));
});
} else {
candidates.sort(function(a, b) {
if (a.band !== b.band) return a.band - b.band;
if (a.score !== b.score) return a.score - b.score;
if (a.y !== b.y) return a.y - b.y;
return a.x - b.x;
});
}
for (let i = 0; i < candidates.length; i++) {
const c = candidates[i];
if (overlapsMaskLight(c.rect, placed, gapX, gapY, componentGap, c.componentId)) {
continue;
}
tag.style.left = Math.round(c.x) + 'px';
tag.style.top = Math.round(c.y) + 'px';
tag.style.visibility = 'visible';
tag.style.opacity = '1';
placed.push({
left: c.rect.left,
top: c.rect.top,
right: c.rect.right,
bottom: c.rect.bottom,
componentId: c.componentId
});
placedThisTag = true;
break;
}
if (!placedThisTag) {
tag.style.display = 'none';
}
});
}
function runMaskLightAll() {
const fillClouds = document.querySelectorAll('.custom-tag-cloud-text-mask-light-fill');
const randomClouds = document.querySelectorAll('.custom-tag-cloud-text-mask-light-random-tight');
fillClouds.forEach(function(cloud) {
positionMaskLightCloud(cloud, false);
});
randomClouds.forEach(function(cloud) {
positionMaskLightCloud(cloud, true);
});
}
runMaskLightAll();
// Webフォント・日本語フォントの読み込み後に再配置
if (document.fonts && document.fonts.ready) {
document.fonts.ready.then(function() {
runMaskLightAll();
});
}
let maskLightResizeTimer;
window.addEventListener('resize', function () {
clearTimeout(maskLightResizeTimer);
maskLightResizeTimer = setTimeout(function () {
runMaskLightAll();
}, 200);
});
});
</script>
<?php
}
add_action('wp_footer', 'custom_tag_cloud_text_mask_light_scripts');
SWELLでは、管理画面の「外観」→「カスタマイズ」→「追加CSS」へ進みます。既存のCSSコード下へ追加して、保存・公開してください。

CSSコード全文
/* ================================================
自由文字タグクラウド 軽量版
外周回転なし
横表示 / 縦表示 / smallestだけ横表示
letter_spacing / gap_x / gap_y / component_gap 対応
スマホ時のタグ文字縮小はPHP+JavaScript側で制御
================================================ */
.custom-tag-cloud-text-mask-light-fill,
.custom-tag-cloud-text-mask-light-random-tight {
position: relative;
width: min(var(--tag-cloud-text-mask-light-width, 600px), 100%);
aspect-ratio: var(--tag-cloud-text-mask-light-aspect, 1 / 1);
height: auto;
margin: 0 auto;
padding: 4px;
overflow: hidden;
box-sizing: border-box;
line-height: 1;
}
/* 全タグ共通 */
.custom-tag-cloud-text-mask-light-fill a,
.custom-tag-cloud-text-mask-light-random-tight a {
display: inline-block;
font-weight: bold;
text-decoration: none;
color: #04384C;
line-height: 1.1;
vertical-align: middle;
margin: 0;
padding: 0;
transition: color 0.3s;
word-break: keep-all;
white-space: nowrap;
letter-spacing: var(--tag-cloud-text-mask-light-letter-spacing, 0);
}
/* 縦表示 */
.custom-tag-cloud-text-mask-light-fill[data-mask-light-vertical="1"] a,
.custom-tag-cloud-text-mask-light-random-tight[data-mask-light-vertical="1"] a {
writing-mode: vertical-rl;
text-orientation: upright;
line-height: 1;
white-space: nowrap;
letter-spacing: var(--tag-cloud-text-mask-light-letter-spacing, 0);
}
/* 縦表示でも、smallestのタグだけ横表示 */
.custom-tag-cloud-text-mask-light-fill[data-mask-light-vertical="1"] a.is-smallest-horizontal,
.custom-tag-cloud-text-mask-light-random-tight[data-mask-light-vertical="1"] a.is-smallest-horizontal {
writing-mode: horizontal-tb;
text-orientation: mixed;
white-space: nowrap;
line-height: 1.1;
letter-spacing: var(--tag-cloud-text-mask-light-letter-spacing, 0);
}
/* smallest横表示の保険 */
.custom-tag-cloud-text-mask-light-fill a.is-smallest-horizontal,
.custom-tag-cloud-text-mask-light-random-tight a.is-smallest-horizontal {
writing-mode: horizontal-tb;
text-orientation: mixed;
white-space: nowrap;
letter-spacing: var(--tag-cloud-text-mask-light-letter-spacing, 0);
}
/* ホバー */
.custom-tag-cloud-text-mask-light-fill a:hover,
.custom-tag-cloud-text-mask-light-random-tight a:hover {
color: #ff6600 !important;
text-decoration: underline;
}
/* 色指定例 */
.custom-tag-cloud-text-mask-light-fill .custom-tag-occupation,
.custom-tag-cloud-text-mask-light-random-tight .custom-tag-occupation {
color: #EE82EE !important;
}
.custom-tag-cloud-text-mask-light-fill .custom-tag-hobby,
.custom-tag-cloud-text-mask-light-random-tight .custom-tag-hobby {
color: #FFFF00 !important;
}
.custom-tag-cloud-text-mask-light-fill .custom-tag-expression9,
.custom-tag-cloud-text-mask-light-random-tight .custom-tag-expression9 {
color: #0000FF !important;
}
/* スマホ表示 */
@media screen and (max-width: 600px) {
.custom-tag-cloud-text-mask-light-fill,
.custom-tag-cloud-text-mask-light-random-tight {
width: 85%;
}
}
投稿・固定ページの編集画面で「ショートコード」ブロックを追加し、上の1行を入力します。ブロックは、追加ボタンから「ショートコード」を検索すると見つけられます。

記事中に設置したい場合
ショートコード例
[custom_tag_cloud_text_mask_light_fill text="や" width="600" height="600" font_size="520" stroke_width="5" vertical="0" smallest="8" largest="24" letter_spacing="-0.05em" gap_x="0" gap_y="0" component_gap="18" step="3"]

だいたい「や」に見えます。タグ数は自動的に減っています。
タイポグラフィー風タグクラウド「や」
何も指定しない場合
[custom_tag_cloud_text_mask_light_random_tight text="中" width="600" height="600" font_size="520" stroke_width="10" smallest="4" largest="36"]
この場合は、今までのCSS色のままです。
一部のタグだけウィジェットで色指定
[custom_tag_cloud_text_mask_light_random_tight text="中" width="600" height="600" font_size="520" stroke_width="10" smallest="4" largest="36" tag_colors="occupation:#EE82EE,hobby:#FFFF00,expression9:#0000FF"]
この場合は、
occupation → #EE82EE
hobby → #FFFF00
expression9 → #0000FF
それ以外 → 今までのCSS色のまま
です。
縦表示・分離文字・タグ別カラー
[custom_tag_cloud_text_mask_light_random_tight text="う" width="600" height="600" font_size="560" stroke_width="10" font_family="sans-serif" vertical="1" smallest="4" largest="36" letter_spacing="-0.05em" gap_x="2" gap_y="0" component_gap="20" tag_colors="occupation:#EE82EE,hobby:#FFFF00,expression9:#0000FF"]
タグ色を一時的に変えたい場合
CSSを触らず、ショートコードだけ変えればOKです。
[custom_tag_cloud_text_mask_light_random_tight text="山" width="600" height="600" font_size="540" stroke_width="10" vertical="1" smallest="4" largest="36" tag_colors="occupation:#cc3366,hobby:#ffaa00,expression9:#3366cc"]
これで、同じタグクラウドでも、置く場所ごとにタグ色を変えられます。
改行指定
はい、ウィジェット内のショートコードで指定できるということです。
ただし、整理するとこうです。
今のコードで指定できるもの
これは、すでにウィジェットで指定できます。
text="AB"
例です。
[custom_tag_cloud_text_mask_light_random_tight text="AB" width="600" height="600" font_size="250" smallest="4" largest="36"]
これは ABを横1行の文字シルエットとして表示します。
まだ指定できないもの
これは、今のコードのままではまだできません。
text="AB|CD"
つまり、
AB
CD
のように、Bの後で改行して表示する機能は、まだ入っていません。
複数行対応にすれば、ウィジェットで指定できる
コードを少し変更すれば、ウィジェットでこう指定できるようにできます。
[custom_tag_cloud_text_mask_light_random_tight text="AB|CD" width="600" height="1200" font_size="250" smallest="4" largest="36"]
この場合、| を改行扱いにして、
AB
CD
の形で文字シルエットを作る、という仕様にできます。
結論としては、
text="AB"
は今のコードでウィジェット指定可能。
text="AB|CD"
は、複数行対応コードを追加すればウィジェット指定可能です。
縦表示
はい、同じようにできます。
ただし、ここは少し分けて考えた方がいいです。
今の vertical="1" の意味
今のコードの
vertical="1"
は、タグそのものを縦書き表示にする指定です。
つまり、
[custom_tag_cloud_text_mask_light_random_tight text="AB" vertical="1"]
とした場合、文字シルエットは基本的に
AB
の横並びで作られます。
その中に入るタグが縦書きになります。
文字シルエット自体を縦にしたい場合
たとえば、
A
B
のように、ABそのものを縦に並べた形にしたいなら、今のコードにはまだその機能は入っていません。
ただし、追加すればウィジェットで指定できます。
一番わかりやすい方法は、前に話したように | を改行扱いにする方法です。
text="A|B"
すると、表示イメージはこうです。
A
B
4文字なら、
text="A|B|C|D"
で、
A
B
C
D
にできます。
「AB」「CD」を縦に2段表示するなら
これは、
text="AB|CD"
です。
表示イメージは、
AB
CD
になります。
つまり、縦長の箱にしたいなら、
[custom_tag_cloud_text_mask_light_random_tight text="AB|CD" width="600" height="1200" font_size="250"]
のような使い方になります。
ただし、これは複数行対応コードを追加した場合です。
整理すると
vertical="1"
これは、中に入るタグを縦書きにする指定。
text="A|B|C|D"
これは、文字シルエット自体を縦に並べる指定。
なので、最終的にはこういう使い方ができます。
[custom_tag_cloud_text_mask_light_random_tight text="A|B|C|D" width="600" height="1200" font_size="250" vertical="1" smallest="4" largest="36"]
この場合は、
文字シルエット → A B C D を縦に配置
中のタグ → 縦書き
になります。
要するに、縦出力もできますが、今のコードにはまだ「text内の改行処理」が入っていないという状態です。
さまざまな文字で試してみる
今回は実験的な表示例として、6つの文字を使ってタイポグラフィー風のタグクラウドを作成しました。使用した文字は、「や」「り」「た」「い」「こ」「と」です。
この記事で実際のタグクラウドとして表示しているのは、下記の「や」のみで、ほかの文字の表示例は画像で掲載します。
だいたい「や」に見えます。
タイポグラフィー風タグクラウド「や」
タイポグラフィー風タグクラウド「や」

タイポグラフィー風タグクラウド「り」

タイポグラフィー風タグクラウド「た」

タイポグラフィー風タグクラウド「い」

タイポグラフィー風タグクラウド「こ」
※ここに「こ」の画像を掲載

タイポグラフィー風タグクラウド「と」

その他|実験的に作成した図形型・文字型タグクラウド
さまざまなタグクラウドを実験的に作成するにあたり、形の特徴を次の3種類に整理しました。
- 図形型タグクラウド:円、三角形、四角形、線など
- 文字型タグクラウド:ひらがな、カタカナ、漢字、アルファベット、数字など
- 記号型タグクラウド:矢印、数学記号、各種シンボルなど
以下では、作成の参考として、それぞれのタグクラウドの例を紹介します。
図形型タグクラウド
次の6つの図形でタイポグラフィー風の文字型タグクラウドを作成しました。図形によってタグクラウドの印象はかなり変わります。
- ● 丸:やわらかい、安定、親しみやすい
- ◆ ひし形:整った印象、少しデザイン的
- ▶ 右三角:前進、行動、方向性
- ♥ ハート:感情、好み、自己表現
以下が表示する図形型タグクラウドです。
タイポグラフィー風 図形型タグクラウド「● 丸」

丸は、図形型タグクラウドの中でも特に使いやすい形です。角がないため、全体の印象がやわらかくなり、ブログのデザインにもなじみやすいです。主張しすぎないのに、普通のタグ一覧よりは見た目に特徴が出ます。
また、丸は外周が自然につながっているので、タグを中に収めやすく、形も比較的安定して見えます。タグ数が多い場合でも、全体としてまとまりやすいのが利点です。タグが少ないと中心部分に空きができます。
最初に試す図形としては、丸がかなり扱いやすいと感じます。
タイポグラフィー風図形型タグクラウド「◆ ひし形」

ひし形は、丸や四角とは少し違う、デザイン性のある見た目になりやすい図形です。中央に広さがあり、上下左右の先端に向かって細くなっていくため、タグの大きさにメリハリが出やすくなります。中心部に大きめのタグ、周辺に小さめのタグが入ると、形としてまとまりやすいです。
ひし形は「整っているけれど少し動きがある」印象を作れます。単純な四角よりも変化があり、丸ほど柔らかすぎないため、ほどよく装飾感を出したい場合に向いています。
タイポグラフィー風図形型タグクラウド「▶ 右三角」

右三角は、見るだけで進む方向や流れを連想しやすい図形です。そのため、行動、前進、変化、成長、チャレンジといったテーマの記事と相性がよいです。ブログの中でも、読者を次の行動へ促したい位置に置くと、視覚的な意味も持たせやすくなります。
ただし、三角形は先端が細くなるため、タグが入りにくい部分があります。特に先端まで形を出そうとすると、小さいタグの扱いが重要になります。形としてはわかりやすく、方向性を持ったタグクラウドとして印象に残りやすい図形です。
タイポグラフィー風図形型タグクラウド「♥ ハート」

ハート型は、6つの中でも特に感情的な印象を持ちやすい図形です。「好きなこと」「趣味」「お気に入り」「気持ち」「自己表現」など、内面に関わるブログテーマとの相性がよく、見た目にも親しみやすさがあります。
ハートは上のくぼみと下の尖った部分があるため、丸や四角に比べるとやや調整が必要です。余白が多いと形がぼやけやすく、詰めすぎると見づらくなることがあります。それでも、うまくまとまると印象がかなり強く、タグクラウド自体をアイキャッチのように見せやすい図形です。
記号型タグクラウド
次は、タグを記号や図形の形に配置するタイポグラフィー風記号型タグクラウドを作成しました。今回使用する記号は、次の6つです。
- ♣ :クラブ
- ➤: 矢印
- ✚: 十字
- ☾ :月
- ☁ :雲
- ➥: 曲がる矢印
- ➦ :曲がる矢印
- ♨ 温泉
図形とは違い、少し意味や雰囲気を持った記号を使っています。これもタグクラウドを、記事やサイトの印象に合わせたデザイン要素として見せる試みです。
タイポグラフィー風記号型タグクラウド「♣ クラブ」

クラブは、トランプのマークとして知られる記号で、丸が3つ集まり、下に軸が伸びる特徴的な形をしています。丸や四角よりも個性があり、見た目に少し遊び心が出ます。そのため、雑記ブログや趣味系の記事とも相性がよく、ブログに堅すぎない雰囲気を加えたいときに面白い形です。
クラブは構造が少し複雑なので、上部の丸い3つのふくらみと、下の持ち手部分のバランスを取る必要があります。ただ、そのぶん、他の記号にはない独特の見た目になります。ありきたりな形では少し物足りないときに使うと、印象に残りやすいです。
タイポグラフィー風記号型タグクラウド「➤ 矢印」

矢印は、記号として非常に意味がわかりやすい形です。読む人に「次へ」「進む」「向かう」といったイメージを与えやすいため、記事導線との相性がよいのが特徴です。たとえば、関連記事の前、まとめの下、行動を促したい箇所などに置くと、自然に視線を流しやすくなります。
矢印はブログ全体の装飾として使っても、意味が伝わりやすいのが利点です。見た目に変化をつけつつ、ただの飾りで終わらない形ともいえます。先端部分と後方の部分の差があるため、レイアウトの調整は必要ですが、完成すると動きのあるタグクラウドとして表現できます。
タイポグラフィー風記号型タグクラウド「✚ 」十字

十字は、縦と横が交差するわかりやすい形です。タグクラウドとして表示すると、中心に向かってタグが集まるような印象になります。丸や四角よりも形に方向性があり、シンプルながら存在感があります。
十字は、整理、交差、選択、つながり、補足などの印象を持たせやすい記号です。ブログ記事では、考え方を整理する記事や、複数の要素が交わるテーマと相性がよいと感じます。
また、形がはっきりしているため、記号型タグクラウドの中では比較的扱いやすい部類です。記号としてのわかりやすさと、タグクラウドとしての安定感を両立しやすいのが十字です。
タイポグラフィー風記号型タグクラウド「☾」 月

月の形は、少し細く、曲線の印象が強い記号です。丸のように全面を使う図形ではなく、外側のカーブに沿ってタグが配置されるため、余白が印象的に見えます。ぎっしり詰めるというよりも、細いシルエットをどう見せるかが重要になります。
月は、夜、静けさ、思考、内面、余白、落ち着きといったテーマに合いやすい記号です。自己理解の記事や、心の動きについて書いた記事に合わせると、雰囲気が出やすいと思います。
ただし、月は細い部分が多いため、大きなタグばかりだと形が崩れやすくなります。小さいタグを目立たせ、月のカーブが見えるように調整する必要があります。※画像の月の記号の上下の先も少し丸くなってしまっています。
余白を活かした静かなタグクラウドにしたい場合、月の形はかなり面白いです。
タイポグラフィー風記号型タグクラウド「☁ 雲」

雲は、丸みのあるやわらかい形です。タグクラウドとして表示すると、全体がふわっとまとまりやすく、硬さのない見た目になります。丸よりも少し遊びがあり、ハートほど感情的な印象になりすぎないため、使いやすい記号です。
雲は、思考、気分、余白、暮らし、雑記、アイデアなどのテーマと相性があります。特に、考えをまとめる記事や、ゆるやかなライフスタイル記事に合わせると自然です。形としては上部にふくらみがあり、下部は比較的横に広がります。タグの配置がうまくいくと、雲らしい輪郭が出やすいです。
やわらかい雰囲気を出しながら、少し個性も見せたい場合は、雲のタグクラウドが使いやすいです。
タイポグラフィー風記号型タグクラウド「➥ 曲がる 矢印」

➥は、右へ向かうだけでなく、途中で曲がるような印象を持つ矢印です。単純な右矢印とは違い、方向転換や次の流れを感じさせます。記事の中では、考え方を変える、別の視点に進む、次の行動へ移るといったテーマと相性がよいです。
タグクラウドとしては、矢印の軸部分と先端部分のバランスが大切になります。軸が細くなりすぎると形がわかりにくくなり、先端だけが目立ちすぎると矢印全体の流れが崩れます。そのため、タグのサイズや間隔を調整しながら、曲がる方向が伝わるように見せる必要があります。
行動の変化や、次のステップを表現したいときに使いやすい記号です。
タイポグラフィー風記号型タグクラウド「➦ 曲がる矢印」

➦も曲がる矢印ですが、➥とは少し印象が違います。同じ矢印系の記号でも、曲がり方や線の流れによって、見え方は変わります。➦は、戻る、回り込む、方向を変える、別ルートへ進むような印象を持たせやすい記号です。ブログ記事では、やり方を変える、考え方を切り替える、遠回りして進む、別の道を選ぶといったテーマに合わせやすいです。
タグクラウドとしては、形が似ている「➥ 曲がる 矢印」と同じように、タグのサイズや間隔を調整しながら、曲がる方向が伝わるように見せる必要があります。
少しクセのある矢印として、変化や転換を表現したいときに面白い記号です。
タイポグラフィー風記号型タグクラウド「♨ 温泉」

温泉マークは、今回の6つの中でも特に雰囲気が出やすい記号です。丸い湯だまりのような形と、上に立ちのぼる湯気のような線があるため、見るだけでリラックスした印象があります。雑記ブログ、暮らし、休息、疲れ、癒やし、日常の工夫などの記事と相性がよいです。
タグクラウドとしては、下の丸い部分と上の湯気部分を分けて見せる必要があります。下だけが大きくなりすぎると温泉マークらしさが弱まり、上の湯気が細すぎると形が伝わりにくくなります。そのぶん、うまく表示できるとかなり個性的です。普通の図形・記号・文字では出せない、ゆるさと親しみやすさがあります。
暮らし系や雑記系の記事に合わせるなら、温泉マークはかなり使いやすい記号です。
文字型タグクラウド
タイポグラフィー風の文字型タグクラウドを作成しました。使用する文字によって、タグクラウドの印象は大きく変わります。今回は一部で、ブログのタグから文字型タグクラウドを生成するコードではなく、任意のテキストから生成するコードも使用しました。
- 形:形にするで、当ブログのタグで作成した、タイポグラフィー風文字型タグクラウド「形」
- 創:クリエイティブな職業を集めて作成した、タイポグラフィー風文字型タグクラウド「創」
- 伝:伝統に関わる職業を集めて作成した、タイポグラフィー風文字型タグクラウド「伝」
- Q:質問記事用に作成した、タイポグラフィー風文字型タグクラウド「Q」
- T:コード作成過程で作成した、タイポグラフィー風文字型タグクラウド「T」
- to:複数の文字でどうなるか試しに作成した、タイポグラフィー風文字型タグクラウド「to」
- マーク:これも複数の文字でどうなるか試しに作成した、タイポグラフィー風文字型タグクラウド「マーク」
以下が表示する文字型タグクラウドです。
タイポグラフィー風文字型タグクラウド「形」

タイポグラフィー風文字型タグクラウド「創」

この「創」は、さまざまな表現方法に関わる職業や肩書きを集めて作成した、タイポグラフィー風の文字型タグクラウドです。
「創」の中には、デザイナー、イラストレーター、ライター、カメラマン、動画クリエイター、編集者、作曲家、ゲームクリエイターなど、表現や創作に関わる言葉を配置しました。その中でも、ブログに関係する職業や肩書きを大きく表示しています。
タイポグラフィー風文字型タグクラウド「伝」

「伝」という文字には、伝える、受け継ぐ、広めるという意味があります。伝統に関わる職業は、まさに過去から続いてきた技術や文化を、現在に受け継ぎ、次の世代へ渡していく仕事です。
たとえば、木を扱う仕事、布を扱う仕事、食を扱う仕事、音を扱う仕事、舞台で表現する仕事、作法や思想を伝える仕事などがあります。表現方法は違っても、そこには共通して、長く続いてきたものを自分の手や身体を通して形にする行為があります。
そのため、今回は「伝」という文字の中に、さまざまな伝統職業を入れました。
タイポグラフィー風文字型タグクラウド「Q」

Q&Aの「Q」をもとに作成しました。円形を基調とした文字なので、全体的に柔らかい印象があります。形を崩さずにタグを配置するのは難しかったものの、何とかうまく収めることができました。タグクラウドにすることで形の特徴が際立つ、面白い文字だと思います。
タイポグラフィー風文字型タグクラウド「T」
タイポグラフィー風文字型タグクラウド「マーク」

「マ」「ー」「ク」の3文字を使って作成しました。それぞれの形を保ちながらタグがうまく収まり、「マーク」と読める形で表示されています。
一文字で作るタグクラウドとは異なり、複数の文字を横に並べることで、形だけでなく具体的な意味も伝わるようになりました。文字数が増えると配置の調整は難しくなりますが、単語として意味を持たせられる点が面白いと思います。
タイポグラフィー風文字型タグクラウド「to」

まとめ
今回は、SWELLで使える自作カスタマイズとして、図形・記号・文字の形にタグを配置する「図形・記号・文字型タグクラウド」を紹介しました。
通常のタグクラウドは、タグを一覧で見せるためのものです。一方、図形・記号・文字型タグクラウドは、タグをサイトのデザイン要素として見せることができます。好きな図形・記号・文字を指定でき、横表示、縦表示、字間、タグ同士の間隔、分離パーツの間隔なども調整できます。
文字型タグクラウドは、通常のタグクラウドとは少し違う、タイポグラフィーのような見せ方ができるカスタマイズです。ブログの雰囲気に合わせて使えば、タグクラウドを単なる一覧ではなく、記事やサイトを彩るデザイン要素として活用できます。
前回記事















コメント