Skip to content

Commit fe65bfa

Browse files
committed
feat: Enhance MagicMoveText with SEO-friendly semantic markup (#8797)
1 parent 5e181fb commit fe65bfa

3 files changed

Lines changed: 62 additions & 7 deletions

File tree

apps/portal/view/home/parts/hero/Content.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class Content extends BaseContainer {
4141
colorMove: '#3E63DD',
4242
flex : 'none',
4343
reference: 'magic-move',
44+
renderSeoList: true,
4445
replaceWithTextNode: false,
4546
useCache : false,
4647

resources/scss/src/component/MagicMoveText.scss

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,16 @@
3636
.neo-measure-element {
3737
position: relative;
3838
}
39-
}
39+
40+
.neo-seo-list {
41+
border : 0;
42+
clip : rect(0, 0, 0, 0);
43+
height : 1px;
44+
margin : -1px;
45+
overflow : hidden;
46+
padding : 0;
47+
position : absolute;
48+
white-space: nowrap;
49+
width : 1px;
50+
}
51+
}

src/component/MagicMoveText.mjs

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@ class MagicMoveText extends Component {
102102
* @reactive
103103
*/
104104
useCache_: true,
105+
/**
106+
* Set to true to render a semantic `<ul>` containing all cycleTexts for SEO.
107+
* @member {Boolean} renderSeoList_=false
108+
* @reactive
109+
*/
110+
renderSeoList_: false,
105111
/**
106112
* Set to false to not replace the character spans with a text node after the animation.
107113
* @member {Boolean} replaceWithTextNode=true
@@ -114,9 +120,11 @@ class MagicMoveText extends Component {
114120
*/
115121
_vdom:
116122
{style: {}, cn: [
117-
{cls: ['neo-content'], cn: []},
118-
{cls: ['neo-measure-element-wrapper'], removeDom: true, cn: [
119-
{cls: ['neo-measure-element'], cn:[]}
123+
{ariaHidden: true, cn: [
124+
{cls: ['neo-content'], cn: []},
125+
{cls: ['neo-measure-element-wrapper'], removeDom: true, cn: [
126+
{cls: ['neo-measure-element'], cn:[]}
127+
]}
120128
]}
121129
]}
122130
}
@@ -220,7 +228,15 @@ class MagicMoveText extends Component {
220228
* @protected
221229
*/
222230
get measureWrapper() {
223-
return this.vdom.cn[1]
231+
return this.vdom.cn[0].cn[1]
232+
}
233+
/**
234+
* A getter for the visual content container.
235+
* @member {Object} visualWrapper
236+
* @protected
237+
*/
238+
get visualWrapper() {
239+
return this.vdom.cn[0].cn[0]
224240
}
225241

226242
/**
@@ -376,7 +392,7 @@ class MagicMoveText extends Component {
376392
me.previousChars = [];
377393

378394
// Reset a transitioning state
379-
me.vdom.cn[0].cn.length = 0;
395+
me.visualWrapper.cn.length = 0;
380396

381397
await me.afterSetText(value, oldValue)
382398
}
@@ -386,6 +402,32 @@ class MagicMoveText extends Component {
386402
}
387403
}
388404

405+
/**
406+
* Updates the VDOM to include or remove the SEO-friendly list.
407+
* @param {Boolean} value
408+
* @param {Boolean} oldValue
409+
* @protected
410+
*/
411+
afterSetRenderSeoList(value, oldValue) {
412+
let me = this;
413+
414+
if (value) {
415+
if (!me.vdom.cn[1]) {
416+
me.vdom.cn.push({
417+
tag: 'ul',
418+
cls: ['neo-seo-list'],
419+
cn : me.cycleTexts?.map(text => ({tag: 'li', text})) || []
420+
});
421+
me.update();
422+
}
423+
} else {
424+
if (me.vdom.cn[1] && me.vdom.cn[1].cls.includes('neo-seo-list')) {
425+
me.vdom.cn.pop();
426+
me.update();
427+
}
428+
}
429+
}
430+
389431
/**
390432
* Applies the transition time to a CSS variable, allowing the animations to be controlled via JavaScript.
391433
* @param {Number} value The new value of `transitionTime`.
@@ -611,7 +653,7 @@ class MagicMoveText extends Component {
611653
async updateChars() {
612654
let me = this,
613655
{chars, previousChars} = me,
614-
charsContainer = me.vdom.cn[0],
656+
charsContainer = me.visualWrapper,
615657
letters = chars.map(char => char.name),
616658
charNode, index;
617659

0 commit comments

Comments
 (0)