diff --git a/site/content/docs/5.0/components/popovers.md b/site/content/docs/5.0/components/popovers.md
index ccaf1f63f..fed71f21d 100644
--- a/site/content/docs/5.0/components/popovers.md
+++ b/site/content/docs/5.0/components/popovers.md
@@ -198,7 +198,7 @@ Note that for security reasons the `sanitize`, `sanitizeFn`, and `allowList` opt
selector |
string | false |
false |
- If a selector is provided, popover objects will be delegated to the specified targets. In practice, this is used to enable dynamic HTML content to have popovers added. See this and an informative example. |
+ If a selector is provided, popover objects will be delegated to the specified targets. In practice, this is used to enable dynamic HTML content to have popovers added. See {{< gh "issue 4215" >}} and an informative example. |
template |
diff --git a/site/content/docs/5.0/components/tooltips.md b/site/content/docs/5.0/components/tooltips.md
index 8f5aac533..ff7f0094d 100644
--- a/site/content/docs/5.0/components/tooltips.md
+++ b/site/content/docs/5.0/components/tooltips.md
@@ -220,7 +220,7 @@ Note that for security reasons the `sanitize`, `sanitizeFn`, and `allowList` opt
selector |
string | false |
false |
- If a selector is provided, tooltip objects will be delegated to the specified targets. In practice, this is used to also apply tooltips to dynamically added DOM elements (jQuery.on support). See this and an informative example. |
+ If a selector is provided, tooltip objects will be delegated to the specified targets. In practice, this is used to also apply tooltips to dynamically added DOM elements (jQuery.on support). See {{< gh "issue 4215" >}} and an informative example. |
template |
diff --git a/site/content/docs/5.0/getting-started/rtl.md b/site/content/docs/5.0/getting-started/rtl.md
index 4ade8359c..866bbbbb3 100644
--- a/site/content/docs/5.0/getting-started/rtl.md
+++ b/site/content/docs/5.0/getting-started/rtl.md
@@ -169,7 +169,7 @@ While this approach is understandable, please pay attention to the following:
1. When switching `.ltr` and `.rtl`, make sure you add `dir` and `lang` attributes accordingly.
2. Loading both files can be a real performance bottleneck: consider some [optimization]({{< docsref "/customize/optimize" >}}), and maybe try to [load one of those files asynchronously](https://www.filamentgroup.com/lab/load-css-simpler/).
-3. Nesting styles this way will prevent our `form-validation-state()` mixin from working as intended, thus require you tweak it a bit by yourself. [See #31223](https://github.com/twbs/bootstrap/issues/31223).
+3. Nesting styles this way will prevent our `form-validation-state()` mixin from working as intended, thus require you tweak it a bit by yourself. See {{< gh "issue 31223" >}}.
{{< /callout >}}
## The breadcrumb case
diff --git a/site/layouts/shortcodes/gh.html b/site/layouts/shortcodes/gh.html
new file mode 100644
index 000000000..09a719694
--- /dev/null
+++ b/site/layouts/shortcodes/gh.html
@@ -0,0 +1,25 @@
+{{- /*
+ Usage: gh "foo", where foo can be:
+
+ * "pr 1" -> #1
+ * "issue 2" -> #2
+ * "@XhmikosR" -> @XhmikosR
+ * "a3b09530fb31cfe6bd14d49965c19e8f89c1b023" -> a3b0953
+ * "a3b0953" -> a3b0953
+
+ Quotes are not always needed, but Hugo can choke on some characters like @, in which case wrap the parameter with quotes. Also, they are needed for PR and Issue
+*/ -}}
+
+{{ range .Params -}}
+ {{- if hasPrefix . "@" -}}
+ {{ . }}
+ {{- else if hasPrefix (. | lower) "pr" -}}
+ #{{ trim (substr . 2) " " }}
+ {{- else if hasPrefix (. | lower) "issue" -}}
+ #{{ trim (substr . (len "issue")) " " }}
+ {{- else if or (eq (len .) 7) (eq (len .) 40) -}}
+ {{ trim (substr . 0 7) " " }}
+ {{- else -}}
+ {{ errorf `Wrong parameter passed in %q shortcode: %s` $.Name $.Position }}
+ {{- end -}}
+{{- end -}}