具有视觉效果的语义歌曲标记 "chord stacking"

Semantic song markup with visual "chord stacking"

我正在寻找一种基于 HTML 的表示形式,用于在音节上方带有和弦的歌曲文本,采用以下方式:

      Am   C        D     F
There is a house in New Orleans

现在,我不在乎 HTML 到底长什么样子,但有一个重要的约束:目的不仅是显示,而且是以语义上有意义的表示形式存储。具体来说,这些歌曲是从 LaTeX-based 语法转换而来的,应该可以以其原始形式恢复,并且在 HTML 中可读且易于处理。所以没有仅仅为了演示目的而花哨的结构,也没有 JavaScript(是的,我已经看过 UltimateGuitar 是如何做到的,这完全不是我想要的)。我需要每个元素都与某个逻辑部分直接对应,就像原始格式一样。

另一方面,这让我很难设计一个呈现堆叠和弦的 CSS。基本规则是你有一个由文本部分和和弦部分组成的“盒子”,整个盒子的宽度应该是两个部分的最大值,每个部分可以包含任意长的文本:

Am         Cmaj7/G  Am G        F Am/G      E7  F#sus4 A
longstuff  short    multichord  more   end.     e  -   ternal

装箱结构是预先知道的,所以你可以假设它是给定的:

((Am) (longstuff)) 
((Cmaj7/G) (short)) 
((Am G) (multichord))
((F Am/G) (more))
end.
((E7) ())
((F#sus4 A) (e))ternal

请注意,在最后一个框中,单词被连字符连接以补偿第一个音节上的两个和弦。我担心这会使 CSS 变得非常困难,因此它可能会以稍微不同的方式表示,例如

((F#sus4 A) (e) (ternal))

你可以想出替代方案,只要它们仍然具有简洁的语义。这是我到目前为止的想法:

.verse {
    line-height: 3rem;
    font-family: serif;
}

.cbox {
    border: 1px solid;
}

.chord {
    position: absolute;
    transform: translate(0, -1.1rem);
    font-weight: bold;
    font-family: monospace;
    user-select: none;
    font-size: large;
}
<p class="verse">
    Test
    <span class="cbox"><span class="chord">Abc</span>X</span>
    <span class="cbox"><span class="chord">Abc</span>Xyzwv</span>
    <span class="cbox"><span class="chord">Abc</span>Xyzw</span>abc
    sd
    <br/>
    Test
    <span class="cbox"><span class="chord">Abc De</span>Xy</span>zwv
    sd
    <br/>
    <span class="cbox"><span class="chord">Ab</span>xyz</span>
    <span class="cbox"><span class="chord">Abc#sus4/C</span>Zyzw xyz</span>
    <span class="cbox"><span class="chord">Am</span>xyzw</span>
    <br/>
    <span class="cbox"><span class="chord">D</span>xyz</span>
    <span class="cbox""><span class="chord">E7</span></span>
    two
    <span class="cbox"><span class="chord">E7</span>th</span>ree
</p>

选择尺寸和边框进行调试。如您所见,顶部(弦)部分的宽度未被考虑在内(因为 position: absolute 阻止了这一点)。

我尝试了其他一些变体,包括这个:<span class="cbox" data-w="2" data-c="Am">longstuff</span>,其中 data-w 是和弦名称中的字母数,用于跨度的 min-width , 并且 data-c 被放入 before 伪元素中,但我仍然没有成功获得正确的宽度。

关于断字问题,我完全不知道。

我可能会使用 XHTML,虽然我想这不会有太大的不同。

我建议使用 CSS grid 来避免重叠。

你可以指定一个1列2行的网格模板,然后用classes告诉内容应该放在哪一行。网格将很好地填充并根据需要创建隐式新列。如果您有一系列连续的和弦或文本,它甚至可以工作,而无需在包装元素中包装 chord/text 对。

对于断字,如果可能的话,我会在需要断字的音节中添加额外的 class,然后使用 CSS.

中的伪元素创建连字符

这是一个工作示例。希望这会有所帮助。这是一个有趣的挑战。

.line {
  display: grid;
  justify-content: start;
  grid-template-columns: auto;
  grid-template-rows: auto auto;
  grid-auto-flow: column;
  gap: 0 0.6em;
  margin-bottom: 1em;
}
.chord {
  grid-row-start: 1;
}
.text {
  grid-row-start: 2;
}
.hyphenated:after {
  content: ' - '
}
<span class="line">
  <span class="chord">Am</span>
  <span class="text">longstuff</span>
  <span class="chord">Cmaj7/G</span>
  <span class="text">short</span>
  <span class="chord">Am G</span>
  <span class="text">multichord</span>
  <span class="chord">F Am/G</span>
  <span class="text">more</span>
  <span class="text">end.</span>
  <span class="chord">E7</span>
  <span class="chord">F#sus4 A</span>
  <span class="text hyphenated">e</span>
  <span class="text">ternal</span>
</span>

<hr>

<p class="verse">
  <span class="line">
    <span class="chord">C</span>
    <span class="text">Frosty the</span>
    <span class="chord">C7</span>
    <span class="text">snowman</span>
  </span>
  <span class="line">
    <span class="text">was a</span>
    <span class="chord">F</span>
    <span class="text">jolly</span>
    <span class="chord">F#dim</span>
    <span class="text">happy</span>
    <span class="chord">C</span>
    <span class="text">soul</span>
    <span class="chord">C7</span>
  </span>
  <span class="line">
    <span class="text">with a</span>
    <span class="chord">F</span>
    <span class="text">corncob</span>
    <span class="chord">F#dim</span>
    <span class="text">pipe and a</span>
    <span class="chord">C</span>
    <span class="text">button</span>
    <span class="chord">A7</span>
    <span class="text">nose</span>
  </span>
  <span class="line">
    <span class="text">and two</span>
    <span class="chord">Dm7</span>
    <span class="text">eyes made</span>
    <span class="chord">G7</span>
    <span class="text">out of</span>
    <span class="chord">C</span>
    <span class="text">coal.</span>
    <span class="chord">C7</span>
  </span>
</p>


编辑:上述方法的一个缺点是,如果没有样式,内容很难理解。

更多语义方法可以将CSS网格与自定义data-*属性和CSS变量回退中定义的内容结合起来。这样,和弦将作为属性存储,而不是散布在歌词中的标记文本。

.verse {
  line-height: 2;
}
.lyric {
  display: inline-grid;
  grid-template-columns: auto;
  grid-template-rows: auto auto;
  line-height: 1;
}
.lyric[data-chord] {
  --chord: attr(data-chord);
}
.lyric:before {
  content: var(--chord, '[=12=]a0');
}
<p class="verse">
  <span class="lyric" data-chord="C">Frosty the</span>
  <span class="lyric" data-chord="C7">snowman</span>
  <br>
  <span class="lyric">was a</span>
  <span class="lyric" data-chord="F">jolly</span>
  <span class="lyric" data-chord="F#dim">happy</span>
  <span class="lyric" data-chord="C">soul</span>
  <span class="lyric" data-chord="C7"></span>
  <br>
  <span class="lyric">with a</span>
  <span class="lyric" data-chord="F">corncob</span>
  <span class="lyric" data-chord="F#dim">pipe and a</span>
  <span class="lyric" data-chord="C">button</span>
  <span class="lyric" data-chord="A7">nose</span>
  <br>
  <span class="lyric">and two</span>
  <span class="lyric" data-chord="Dm7">eyes made</span>
  <span class="lyric" data-chord="G7">out of</span>
  <span class="lyric" data-chord="C">coal.</span>
  <span class="lyric" data-chord="C7"></span>
</p>

这会为每个 .lyric 跨度创建一个单列、两行的内联网格。 --chord 的值设置为 data-chord 属性 的值,仅在具有 属性 的元素上。 然后使用在 all .lyric 元素中设置 :before 伪元素的内容,如果变量是不明确的。这很重要,因为它会将 .lyric 没有和弦的跨度的文本推入底行,并使文本保持水平对齐。