@Paul the best way to avoid element ID’s on html and use CSS modules for classes, where the class name is replaced by a generic hash.
For my add-ons, I use VueJS (you can also use React) components with included HTML templates and modularised CSS. My velocity templates are limited to placeholders. For instance, a regular page would be
$webResourceManager.requireResourcesForContext("myContext")
<html>
<body>
<router-view></router-view>
</body>
</html>
Where is a placeholder used by Vue-Router. Vue will then use my routing definitions (based on URL paths) to load the correct component.
This velocity template is referenced in the atlassian-plugin.xml
like this:
<webwork1 key="myActions" name="${project.name} - WebWork Actions" roles-required="use">
<actions>
<action name="com.atlassian.jira.web.action.JiraWebActionSupport" alias="myAlias">
<view name="success">${resources.dir}/template.vm</view>
</action>
</actions>
</webwork1>
or if you want to add a web-panel:
<web-panel key="myPanel" name="myPanel" location="webpanels.admin.summary.right-panels" weight="161">
<label key="My Panel" />
<resource name="view" type="velocity" location="${resources.dir}/template.vm" />
</web-panel>
The VueJS static resources are bundled using WebPack and included as a single resource:
<resource type="download" name="myResources.js" location="${resources.dir}/static/myResources.js">
<property key="content-type" value="text/javascript"/>
</resource>
Hope this helps!